aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Rakefile3
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb2
-rw-r--r--actionpack/lib/action_controller/base/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/base/mime_responds.rb262
-rw-r--r--actionpack/lib/action_controller/base/renderer.rb3
-rw-r--r--actionpack/lib/action_controller/base/request_forgery_protection.rb3
-rw-r--r--actionpack/lib/action_controller/routing.rb2
-rw-r--r--actionpack/lib/action_controller/routing/generation/url_rewriter.rb2
-rw-r--r--actionpack/lib/action_controller/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_controller/testing/test_case.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb6
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb42
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb30
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb10
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb42
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb97
-rw-r--r--actionpack/test/controller/mime_responds_test.rb204
-rw-r--r--actionpack/test/dispatch/request_test.rb51
-rw-r--r--actionpack/test/fixtures/public/.gitignore2
-rw-r--r--actionpack/test/fixtures/respond_with/using_defaults.html.erb1
-rw-r--r--actionpack/test/fixtures/respond_with/using_defaults.js.rjs1
-rw-r--r--actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs1
-rw-r--r--actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder1
-rw-r--r--actionpack/test/fixtures/respond_with/using_resource.html.erb1
-rw-r--r--actionpack/test/template/active_record_helper_i18n_test.rb7
-rw-r--r--actionpack/test/template/active_record_helper_test.rb10
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb8
-rw-r--r--activemodel/lib/active_model/naming.rb3
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb8
-rw-r--r--activemodel/test/cases/serializeration/xml_serialization_test.rb23
-rw-r--r--activemodel/test/models/contact.rb4
-rw-r--r--activerecord/lib/active_record.rb13
-rw-r--r--activerecord/lib/active_record/association_preload.rb4
-rwxr-xr-xactiverecord/lib/active_record/associations.rb76
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb309
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb33
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb187
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb44
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb37
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb116
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb60
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb37
-rwxr-xr-xactiverecord/lib/active_record/base.rb145
-rw-r--r--activerecord/lib/active_record/callbacks.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb8
-rw-r--r--activerecord/lib/active_record/dirty.rb186
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb6
-rw-r--r--activerecord/lib/active_record/named_scope.rb7
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb2
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb15
-rw-r--r--activerecord/lib/active_record/validations.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb4
-rwxr-xr-xactiverecord/test/cases/base_test.rb5
-rw-r--r--activerecord/test/cases/named_scope_test.rb4
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb6
-rw-r--r--activerecord/test/models/topic.rb2
-rw-r--r--activesupport/lib/active_support/buffered_logger.rb4
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb6
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb2
-rw-r--r--activesupport/lib/active_support/inflector.rb6
-rw-r--r--activesupport/lib/active_support/mini.rb4
-rw-r--r--activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb110
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb2
-rw-r--r--railties/README91
-rw-r--r--railties/Rakefile204
-rwxr-xr-xrailties/bin/rails13
-rwxr-xr-xrailties/guides/files/javascripts/guides.js1
-rw-r--r--railties/guides/images/fxn.jpgbin17773 -> 0 bytes
-rw-r--r--railties/guides/images/fxn.pngbin0 -> 20664 bytes
-rw-r--r--railties/guides/source/action_controller_overview.textile33
-rw-r--r--railties/guides/source/action_mailer_basics.textile30
-rw-r--r--railties/guides/source/active_record_basics.textile6
-rw-r--r--railties/guides/source/active_record_querying.textile17
-rw-r--r--railties/guides/source/active_support_overview.textile818
-rw-r--r--railties/guides/source/activerecord_validations_callbacks.textile8
-rw-r--r--railties/guides/source/ajax_on_rails.textile94
-rw-r--r--railties/guides/source/association_basics.textile53
-rw-r--r--railties/guides/source/caching_with_rails.textile262
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/contribute.textile2
-rw-r--r--railties/guides/source/contributing_to_rails.textile11
-rw-r--r--railties/guides/source/credits.erb.textile6
-rw-r--r--railties/guides/source/debugging_rails_applications.textile2
-rw-r--r--railties/guides/source/form_helpers.textile28
-rw-r--r--railties/guides/source/getting_started.textile129
-rw-r--r--railties/guides/source/i18n.textile83
-rw-r--r--railties/guides/source/index.erb.textile2
-rw-r--r--railties/guides/source/layouts_and_rendering.textile38
-rw-r--r--railties/guides/source/migrations.textile12
-rw-r--r--railties/guides/source/performance_testing.textile2
-rw-r--r--railties/guides/source/plugins.textile2
-rw-r--r--railties/guides/source/rails_application_templates.textile232
-rw-r--r--railties/guides/source/rails_on_rack.textile4
-rw-r--r--railties/guides/source/routing.textile8
-rw-r--r--railties/guides/source/security.textile6
-rw-r--r--railties/guides/source/testing.textile18
-rw-r--r--railties/lib/commands/destroy.rb12
-rwxr-xr-xrailties/lib/commands/generate.rb12
-rw-r--r--railties/lib/commands/update.rb12
-rw-r--r--railties/lib/generators.rb300
-rw-r--r--railties/lib/generators/action_orm.rb74
-rw-r--r--railties/lib/generators/actions.rb263
-rw-r--r--railties/lib/generators/active_record.rb57
-rw-r--r--railties/lib/generators/active_record/migration/migration_generator.rb25
-rw-r--r--railties/lib/generators/active_record/migration/templates/migration.rb (renamed from railties/lib/rails_generator/generators/components/migration/templates/migration.rb)2
-rw-r--r--railties/lib/generators/active_record/model/model_generator.rb35
-rw-r--r--railties/lib/generators/active_record/model/templates/migration.rb (renamed from railties/lib/rails_generator/generators/components/model/templates/migration.rb)4
-rw-r--r--railties/lib/generators/active_record/model/templates/model.rb (renamed from railties/lib/rails_generator/generators/components/model/templates/model.rb)2
-rw-r--r--railties/lib/generators/active_record/observer/observer_generator.rb15
-rw-r--r--railties/lib/generators/active_record/observer/templates/observer.rb (renamed from railties/lib/rails_generator/generators/components/observer/templates/observer.rb)0
-rw-r--r--railties/lib/generators/active_record/session_migration/session_migration_generator.rb20
-rw-r--r--railties/lib/generators/active_record/session_migration/templates/migration.rb (renamed from railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb)2
-rw-r--r--railties/lib/generators/base.rb302
-rw-r--r--railties/lib/generators/erb.rb8
-rw-r--r--railties/lib/generators/erb/controller/controller_generator.rb21
-rw-r--r--railties/lib/generators/erb/controller/templates/view.html.erb2
-rw-r--r--railties/lib/generators/erb/mailer/mailer_generator.rb20
-rw-r--r--railties/lib/generators/erb/mailer/templates/view.erb3
-rw-r--r--railties/lib/generators/erb/scaffold/scaffold_generator.rb54
-rw-r--r--railties/lib/generators/erb/scaffold/templates/edit.html.erb (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/index.html.erb (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb)4
-rw-r--r--railties/lib/generators/erb/scaffold/templates/layout.html.erb (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/new.html.erb (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb)0
-rw-r--r--railties/lib/generators/erb/scaffold/templates/show.html.erb (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb)4
-rw-r--r--railties/lib/generators/generated_attribute.rb (renamed from railties/lib/rails_generator/generated_attribute.rb)16
-rw-r--r--railties/lib/generators/migration.rb65
-rw-r--r--railties/lib/generators/named_base.rb162
-rw-r--r--railties/lib/generators/rails/app/USAGE (renamed from railties/lib/rails_generator/generators/applications/app/USAGE)0
-rw-r--r--railties/lib/generators/rails/app/app_generator.rb210
-rw-r--r--railties/lib/generators/rails/app/templates/README243
-rwxr-xr-xrailties/lib/generators/rails/app/templates/Rakefile (renamed from railties/fresh_rakefile)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb (renamed from railties/helpers/application_controller.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb (renamed from railties/helpers/application_helper.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/models/.empty_directory (renamed from railties/configs/empty.log)0
-rw-r--r--railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory (renamed from railties/html/favicon.ico)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/boot.rb (renamed from railties/environments/boot.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/frontbase.yml (renamed from railties/configs/databases/frontbase.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml (renamed from railties/configs/databases/ibm_db.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/mysql.yml (renamed from railties/configs/databases/mysql.yml)12
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/oracle.yml (renamed from railties/configs/databases/oracle.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/postgresql.yml (renamed from railties/configs/databases/postgresql.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/sqlite2.yml (renamed from railties/configs/databases/sqlite2.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml (renamed from railties/configs/databases/sqlite3.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/environment.rb (renamed from railties/environments/environment.rb)16
-rw-r--r--railties/lib/generators/rails/app/templates/config/environments/development.rb (renamed from railties/environments/development.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/environments/production.rb (renamed from railties/environments/production.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/environments/test.rb (renamed from railties/environments/test.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb (renamed from railties/configs/initializers/backtrace_silencers.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/inflections.rb (renamed from railties/configs/initializers/inflections.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb (renamed from railties/configs/initializers/mime_types.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/new_rails_defaults.rb (renamed from railties/configs/initializers/new_rails_defaults.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt (renamed from railties/configs/initializers/session_store.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/locales/en.yml (renamed from railties/configs/locales/en.yml)0
-rw-r--r--railties/lib/generators/rails/app/templates/config/routes.rb (renamed from railties/configs/routes.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/db/seeds.rb (renamed from railties/configs/seeds.rb)2
-rw-r--r--railties/lib/generators/rails/app/templates/dispatchers/config.ru (renamed from railties/dispatches/config.ru)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/dispatchers/dispatch.fcgi (renamed from railties/dispatches/dispatch.fcgi)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/dispatchers/dispatch.rb (renamed from railties/dispatches/dispatch.rb)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/dispatchers/gateway.cgi (renamed from railties/dispatches/gateway.cgi)2
-rw-r--r--railties/lib/generators/rails/app/templates/doc/README_FOR_APP (renamed from railties/doc/README_FOR_APP)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/404.html (renamed from railties/html/404.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/422.html (renamed from railties/html/422.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/500.html (renamed from railties/html/500.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/favicon.ico (renamed from railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/images/rails.png (renamed from railties/html/images/rails.png)bin6646 -> 6646 bytes
-rw-r--r--railties/lib/generators/rails/app/templates/public/index.html (renamed from railties/html/index.html)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/application.js (renamed from railties/html/javascripts/application.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/controls.js (renamed from railties/html/javascripts/controls.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js (renamed from railties/html/javascripts/dragdrop.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/effects.js (renamed from railties/html/javascripts/effects.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/prototype.js (renamed from railties/html/javascripts/prototype.js)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/robots.txt (renamed from railties/html/robots.txt)0
-rw-r--r--railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory (renamed from railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml)0
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/about.tt (renamed from railties/bin/about)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/console.tt (renamed from railties/bin/console)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/dbconsole.tt (renamed from railties/bin/dbconsole)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/destroy.tt (renamed from railties/bin/destroy)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/generate.tt (renamed from railties/bin/generate)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/performance/benchmarker.tt (renamed from railties/bin/performance/benchmarker)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/performance/profiler.tt (renamed from railties/bin/performance/profiler)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/plugin.tt (renamed from railties/bin/plugin)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/runner.tt (renamed from railties/bin/runner)2
-rwxr-xr-xrailties/lib/generators/rails/app/templates/script/server.tt (renamed from railties/bin/server)2
-rw-r--r--railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory (renamed from railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/functional/.empty_directory (renamed from railties/test/fixtures/lib/generators/missing_class/templates/.gitignore)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/integration/.empty_directory (renamed from railties/test/fixtures/lib/generators/missing_generator/templates/.gitignore)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb (renamed from railties/helpers/performance_test.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/test_helper.rb (renamed from railties/helpers/test_helper.rb)0
-rw-r--r--railties/lib/generators/rails/app/templates/test/unit/.empty_directory (renamed from railties/test/fixtures/lib/generators/missing_templates/.gitignore)0
-rw-r--r--railties/lib/generators/rails/controller/USAGE18
-rw-r--r--railties/lib/generators/rails/controller/controller_generator.rb14
-rw-r--r--railties/lib/generators/rails/controller/templates/controller.rb (renamed from railties/lib/rails_generator/generators/components/controller/templates/controller.rb)0
-rw-r--r--railties/lib/generators/rails/generator/USAGE11
-rw-r--r--railties/lib/generators/rails/generator/generator_generator.rb25
-rw-r--r--railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt5
-rw-r--r--railties/lib/generators/rails/generator/templates/USAGE.tt (renamed from railties/lib/rails_generator/generators/components/plugin/templates/USAGE)0
-rw-r--r--railties/lib/generators/rails/generator/templates/templates/.empty_directory0
-rw-r--r--railties/lib/generators/rails/helper/USAGE17
-rw-r--r--railties/lib/generators/rails/helper/helper_generator.rb13
-rw-r--r--railties/lib/generators/rails/helper/templates/helper.rb (renamed from railties/lib/rails_generator/generators/components/controller/templates/helper.rb)0
-rw-r--r--railties/lib/generators/rails/integration_test/USAGE (renamed from railties/lib/rails_generator/generators/components/integration_test/USAGE)6
-rw-r--r--railties/lib/generators/rails/integration_test/integration_test_generator.rb7
-rw-r--r--railties/lib/generators/rails/mailer/USAGE (renamed from railties/lib/rails_generator/generators/components/mailer/USAGE)5
-rw-r--r--railties/lib/generators/rails/mailer/mailer_generator.rb14
-rw-r--r--railties/lib/generators/rails/mailer/templates/mailer.rb (renamed from railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb)3
-rw-r--r--railties/lib/generators/rails/metal/USAGE (renamed from railties/lib/rails_generator/generators/components/metal/USAGE)0
-rw-r--r--railties/lib/generators/rails/metal/metal_generator.rb11
-rw-r--r--railties/lib/generators/rails/metal/templates/metal.rb (renamed from railties/lib/rails_generator/generators/components/metal/templates/metal.rb)0
-rw-r--r--railties/lib/generators/rails/migration/USAGE (renamed from railties/lib/rails_generator/generators/components/migration/USAGE)4
-rw-r--r--railties/lib/generators/rails/migration/migration_generator.rb8
-rw-r--r--railties/lib/generators/rails/model/USAGE (renamed from railties/lib/rails_generator/generators/components/model/USAGE)15
-rw-r--r--railties/lib/generators/rails/model/model_generator.rb8
-rw-r--r--railties/lib/generators/rails/model_subclass/model_subclass_generator.rb11
-rw-r--r--railties/lib/generators/rails/observer/USAGE (renamed from railties/lib/rails_generator/generators/components/observer/USAGE)7
-rw-r--r--railties/lib/generators/rails/observer/observer_generator.rb7
-rw-r--r--railties/lib/generators/rails/performance_test/USAGE (renamed from railties/lib/rails_generator/generators/components/performance_test/USAGE)6
-rw-r--r--railties/lib/generators/rails/performance_test/performance_test_generator.rb7
-rw-r--r--railties/lib/generators/rails/plugin/USAGE13
-rw-r--r--railties/lib/generators/rails/plugin/plugin_generator.rb47
-rw-r--r--railties/lib/generators/rails/plugin/templates/MIT-LICENSE (renamed from railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/README (renamed from railties/lib/rails_generator/generators/components/plugin/templates/README)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/Rakefile (renamed from railties/lib/rails_generator/generators/components/plugin/templates/Rakefile)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/init.rb (renamed from railties/lib/rails_generator/generators/components/plugin/templates/init.rb)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/install.rb (renamed from railties/lib/rails_generator/generators/components/plugin/templates/install.rb)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt (renamed from railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt (renamed from railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake)0
-rw-r--r--railties/lib/generators/rails/plugin/templates/uninstall.rb (renamed from railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb)0
-rw-r--r--railties/lib/generators/rails/resource/USAGE (renamed from railties/lib/rails_generator/generators/components/resource/USAGE)8
-rw-r--r--railties/lib/generators/rails/resource/resource_generator.rb40
-rw-r--r--railties/lib/generators/rails/scaffold/USAGE (renamed from railties/lib/rails_generator/generators/components/scaffold/USAGE)2
-rw-r--r--railties/lib/generators/rails/scaffold/scaffold_generator.rb12
-rw-r--r--railties/lib/generators/rails/scaffold_controller/USAGE20
-rw-r--r--railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb26
-rw-r--r--railties/lib/generators/rails/scaffold_controller/templates/controller.rb (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb)26
-rw-r--r--railties/lib/generators/rails/session_migration/USAGE8
-rw-r--r--railties/lib/generators/rails/session_migration/session_migration_generator.rb8
-rw-r--r--railties/lib/generators/rails/stylesheets/USAGE5
-rw-r--r--railties/lib/generators/rails/stylesheets/stylesheets_generator.rb9
-rw-r--r--railties/lib/generators/rails/stylesheets/templates/scaffold.css (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/style.css)0
-rw-r--r--railties/lib/generators/test_unit.rb8
-rw-r--r--railties/lib/generators/test_unit/controller/controller_generator.rb14
-rw-r--r--railties/lib/generators/test_unit/controller/templates/functional_test.rb (renamed from railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/helper/helper_generator.rb13
-rw-r--r--railties/lib/generators/test_unit/helper/templates/helper_test.rb (renamed from railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/integration/integration_generator.rb13
-rw-r--r--railties/lib/generators/test_unit/integration/templates/integration_test.rb (renamed from railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/mailer/mailer_generator.rb21
-rw-r--r--railties/lib/generators/test_unit/mailer/templates/fixture3
-rw-r--r--railties/lib/generators/test_unit/mailer/templates/unit_test.rb (renamed from railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/model/model_generator.rb24
-rw-r--r--railties/lib/generators/test_unit/model/templates/fixtures.yml (renamed from railties/lib/rails_generator/generators/components/model/templates/fixtures.yml)0
-rw-r--r--railties/lib/generators/test_unit/model/templates/unit_test.rb (renamed from railties/lib/rails_generator/generators/components/model/templates/unit_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/observer/observer_generator.rb13
-rw-r--r--railties/lib/generators/test_unit/observer/templates/unit_test.rb (renamed from railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/performance/performance_generator.rb13
-rw-r--r--railties/lib/generators/test_unit/performance/templates/performance_test.rb (renamed from railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/plugin/plugin_generator.rb13
-rw-r--r--railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt (renamed from railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb)0
-rw-r--r--railties/lib/generators/test_unit/plugin/templates/test_helper.rb (renamed from railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb)0
-rw-r--r--railties/lib/generators/test_unit/scaffold/scaffold_generator.rb17
-rw-r--r--railties/lib/generators/test_unit/scaffold/templates/functional_test.rb (renamed from railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb)2
-rw-r--r--railties/lib/initializer.rb10
-rw-r--r--railties/lib/rails/configuration.rb48
-rw-r--r--railties/lib/rails/rack/metal.rb8
-rw-r--r--railties/lib/rails_generator.rb38
-rw-r--r--railties/lib/rails_generator/base.rb266
-rw-r--r--railties/lib/rails_generator/commands.rb621
-rw-r--r--railties/lib/rails_generator/generators/applications/app/app_generator.rb263
-rw-r--r--railties/lib/rails_generator/generators/applications/app/scm/git.rb16
-rw-r--r--railties/lib/rails_generator/generators/applications/app/scm/scm.rb8
-rw-r--r--railties/lib/rails_generator/generators/applications/app/scm/svn.rb7
-rw-r--r--railties/lib/rails_generator/generators/applications/app/template_runner.rb401
-rw-r--r--railties/lib/rails_generator/generators/components/controller/USAGE30
-rw-r--r--railties/lib/rails_generator/generators/components/controller/controller_generator.rb43
-rw-r--r--railties/lib/rails_generator/generators/components/controller/templates/view.html.erb2
-rw-r--r--railties/lib/rails_generator/generators/components/helper/USAGE24
-rw-r--r--railties/lib/rails_generator/generators/components/helper/helper_generator.rb25
-rw-r--r--railties/lib/rails_generator/generators/components/helper/templates/helper.rb2
-rw-r--r--railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb4
-rw-r--r--railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb16
-rw-r--r--railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb30
-rw-r--r--railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb3
-rw-r--r--railties/lib/rails_generator/generators/components/mailer/templates/view.erb3
-rw-r--r--railties/lib/rails_generator/generators/components/metal/metal_generator.rb8
-rw-r--r--railties/lib/rails_generator/generators/components/migration/migration_generator.rb20
-rw-r--r--railties/lib/rails_generator/generators/components/model/model_generator.rb45
-rw-r--r--railties/lib/rails_generator/generators/components/model_subclass/USAGE13
-rw-r--r--railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb32
-rw-r--r--railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb3
-rw-r--r--railties/lib/rails_generator/generators/components/observer/observer_generator.rb16
-rw-r--r--railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb16
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/USAGE25
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb39
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/templates/generator.rb8
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb8
-rw-r--r--railties/lib/rails_generator/generators/components/resource/resource_generator.rb76
-rw-r--r--railties/lib/rails_generator/generators/components/resource/templates/controller.rb2
-rw-r--r--railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb8
-rw-r--r--railties/lib/rails_generator/generators/components/resource/templates/helper.rb2
-rw-r--r--railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb4
-rw-r--r--railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb102
-rw-r--r--railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb2
-rw-r--r--railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb4
-rw-r--r--railties/lib/rails_generator/generators/components/session_migration/USAGE10
-rw-r--r--railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb18
-rw-r--r--railties/lib/rails_generator/lookup.rb249
-rw-r--r--railties/lib/rails_generator/manifest.rb53
-rw-r--r--railties/lib/rails_generator/options.rb150
-rw-r--r--railties/lib/rails_generator/scripts.rb89
-rw-r--r--railties/lib/rails_generator/scripts/destroy.rb29
-rw-r--r--railties/lib/rails_generator/scripts/generate.rb7
-rw-r--r--railties/lib/rails_generator/scripts/update.rb12
-rw-r--r--railties/lib/rails_generator/secret_key_generator.rb24
-rw-r--r--railties/lib/rails_generator/simple_logger.rb46
-rw-r--r--railties/lib/rails_generator/spec.rb44
-rw-r--r--railties/lib/tasks/databases.rake6
-rw-r--r--railties/lib/tasks/documentation.rake2
-rw-r--r--railties/lib/tasks/framework.rake68
-rw-r--r--railties/lib/vendor/thor-0.11.3/CHANGELOG.rdoc75
-rw-r--r--railties/lib/vendor/thor-0.11.3/LICENSE20
-rw-r--r--railties/lib/vendor/thor-0.11.3/README.markdown247
-rwxr-xr-xrailties/lib/vendor/thor-0.11.3/bin/rake2thor87
-rwxr-xr-xrailties/lib/vendor/thor-0.11.3/bin/thor7
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor.rb240
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/actions.rb270
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/actions/create_file.rb102
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/actions/directory.rb87
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/actions/empty_directory.rb133
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/actions/file_manipulation.rb195
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/actions/inject_into_file.rb78
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/base.rb516
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/hash_with_indifferent_access.rb75
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/ordered_hash.rb102
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/error.rb27
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/group.rb263
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/invocation.rb172
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/parser.rb4
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/parser/argument.rb67
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/parser/arguments.rb145
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/parser/option.rb132
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/parser/options.rb142
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/runner.rb291
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/shell.rb72
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/shell/basic.rb216
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/shell/color.rb106
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/task.rb108
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/tasks.rb4
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/tasks/install.rb35
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/tasks/package.rb31
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/tasks/spec.rb70
-rw-r--r--railties/lib/vendor/thor-0.11.3/lib/thor/util.rb229
-rw-r--r--railties/test/boot_test.rb2
-rw-r--r--railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb8
-rw-r--r--railties/test/fixtures/lib/generators/fixjour_generator.rb2
-rw-r--r--railties/test/fixtures/lib/generators/rails/javascripts_generator.rb4
-rw-r--r--railties/test/fixtures/lib/generators/working/working_generator.rb2
-rw-r--r--railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb4
-rw-r--r--railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb4
-rw-r--r--railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb4
-rw-r--r--railties/test/fixtures/vendor/gems/mspec/lib/generators/mspec_generator.rb2
-rw-r--r--railties/test/fixtures/vendor/gems/wrong/lib/generators/wrong_generator.rb3
-rw-r--r--railties/test/generator_lookup_test.rb42
-rw-r--r--railties/test/generators/actions_test.rb177
-rw-r--r--railties/test/generators/app_generator_test.rb182
-rw-r--r--railties/test/generators/controller_generator_test.rb80
-rw-r--r--railties/test/generators/generator_generator_test.rb26
-rw-r--r--railties/test/generators/generator_test_helper.rb303
-rw-r--r--railties/test/generators/generators_test_helper.rb101
-rw-r--r--railties/test/generators/helper_generator_test.rb60
-rw-r--r--railties/test/generators/integration_test_generator_test.rb18
-rw-r--r--railties/test/generators/mailer_generator_test.rb52
-rw-r--r--railties/test/generators/metal_generator_test.rb23
-rw-r--r--railties/test/generators/migration_generator_test.rb59
-rw-r--r--railties/test/generators/model_generator_test.rb137
-rw-r--r--railties/test/generators/named_base_test.rb42
-rw-r--r--railties/test/generators/observer_generator_test.rb33
-rw-r--r--railties/test/generators/performance_test_generator_test.rb18
-rw-r--r--railties/test/generators/plugin_generator_test.rb56
-rw-r--r--railties/test/generators/rails_controller_generator_test.rb44
-rw-r--r--railties/test/generators/rails_helper_generator_test.rb36
-rw-r--r--railties/test/generators/rails_mailer_generator_test.rb29
-rw-r--r--railties/test/generators/rails_model_generator_test.rb48
-rw-r--r--railties/test/generators/rails_model_subclass_generator_test.rb15
-rw-r--r--railties/test/generators/rails_resource_generator_test.rb29
-rw-r--r--railties/test/generators/rails_scaffold_generator_test.rb150
-rw-r--r--railties/test/generators/rails_template_runner_test.rb216
-rw-r--r--railties/test/generators/resource_generator_test.rb106
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb112
-rw-r--r--railties/test/generators/scaffold_generator_test.rb129
-rw-r--r--railties/test/generators/session_migration_generator_test.rb23
-rw-r--r--railties/test/generators/stylesheets_generator_test.rb24
-rw-r--r--railties/test/generators_test.rb156
-rw-r--r--railties/test/initializer_test.rb93
-rw-r--r--railties/test/metal_test.rb30
-rw-r--r--railties/test/rails_generator_test.rb148
-rw-r--r--railties/test/secret_key_generation_test.rb38
410 files changed, 11767 insertions, 5713 deletions
diff --git a/.gitignore b/.gitignore
index 5357fbe481..2d879499fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@ actionmailer/pkg
activesupport/pkg
railties/pkg
railties/test/500.html
+railties/test/fixtures/tmp
+railties/test/initializer/root/log
railties/doc/guides/html/images
railties/doc/guides/html/stylesheets
benches
diff --git a/Rakefile b/Rakefile
index fbb7f2213c..5dd9f40310 100644
--- a/Rakefile
+++ b/Rakefile
@@ -38,7 +38,8 @@ Rake::RDocTask.new do |rdoc|
rdoc.rdoc_files.include('railties/CHANGELOG')
rdoc.rdoc_files.include('railties/MIT-LICENSE')
rdoc.rdoc_files.include('railties/README')
- rdoc.rdoc_files.include('railties/lib/{*.rb,commands/*.rb,rails/*.rb,rails_generator/*.rb}')
+ rdoc.rdoc_files.include('railties/lib/{*.rb,commands/*.rb,rails/*.rb,generators/*.rb}')
+ rdoc.rdoc_files.exclude('railties/lib/vendor/*')
rdoc.rdoc_files.include('activerecord/README')
rdoc.rdoc_files.include('activerecord/CHANGELOG')
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb
index 22b0a126ca..def663b233 100644
--- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb
+++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb
@@ -1,6 +1,6 @@
=begin rdoc
-= Obsolete methods that are depriciated
+= Obsolete methods that are deprecated
If you really want to see them, go to lib/tmail/obsolete.rb and view to your
heart's content.
diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb
index 2519f55269..525787bf92 100644
--- a/actionpack/lib/action_controller/base/http_authentication.rb
+++ b/actionpack/lib/action_controller/base/http_authentication.rb
@@ -276,7 +276,7 @@ module ActionController
#
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
# key from the Rails session secret generated upon creation of project. Ensures
- # the time cannot be modifed by client.
+ # the time cannot be modified by client.
def nonce(time = Time.now)
t = time.to_i
hashed = [t, secret_key]
diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb
index ed0d58dba1..f4a4007a43 100644
--- a/actionpack/lib/action_controller/base/mime_responds.rb
+++ b/actionpack/lib/action_controller/base/mime_responds.rb
@@ -1,5 +1,55 @@
module ActionController #:nodoc:
module MimeResponds #:nodoc:
+ extend ActiveSupport::Concern
+
+ included do
+ class_inheritable_reader :mimes_for_respond_to
+ clear_respond_to
+ end
+
+ module ClassMethods
+ # Defines mimes that are rendered by default when invoking respond_with.
+ #
+ # Examples:
+ #
+ # respond_to :html, :xml, :json
+ #
+ # All actions on your controller will respond to :html, :xml and :json.
+ #
+ # But if you want to specify it based on your actions, you can use only and
+ # except:
+ #
+ # respond_to :html
+ # respond_to :xml, :json, :except => [ :edit ]
+ #
+ # The definition above explicits that all actions respond to :html. And all
+ # actions except :edit respond to :xml and :json.
+ #
+ # You can specify also only parameters:
+ #
+ # respond_to :rjs, :only => :create
+ #
+ def respond_to(*mimes)
+ options = mimes.extract_options!
+
+ only_actions = Array(options.delete(:only))
+ except_actions = Array(options.delete(:except))
+
+ mimes.each do |mime|
+ mime = mime.to_sym
+ mimes_for_respond_to[mime] = {}
+ mimes_for_respond_to[mime][:only] = only_actions unless only_actions.empty?
+ mimes_for_respond_to[mime][:except] = except_actions unless except_actions.empty?
+ end
+ end
+
+ # Clear all mimes in respond_to.
+ #
+ def clear_respond_to
+ write_inheritable_attribute(:mimes_for_respond_to, ActiveSupport::OrderedHash.new)
+ end
+ end
+
# Without web-service support, an action which collects the data for displaying a list of people
# might look something like this:
#
@@ -92,50 +142,187 @@ module ActionController #:nodoc:
# environment.rb as follows.
#
# Mime::Type.register "image/jpg", :jpg
- def respond_to(*types, &block)
- raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
- block ||= lambda { |responder| types.each { |type| responder.send(type) } }
- responder = Responder.new(self)
- block.call(responder)
- responder.respond
- end
+ #
+ # Respond to also allows you to specify a common block for different formats by using any:
+ #
+ # def index
+ # @people = Person.find(:all)
+ #
+ # respond_to do |format|
+ # format.html
+ # format.any(:xml, :json) { render request.format.to_sym => @people }
+ # end
+ # end
+ #
+ # In the example above, if the format is xml, it will render:
+ #
+ # render :xml => @people
+ #
+ # Or if the format is json:
+ #
+ # render :json => @people
+ #
+ # Since this is a common pattern, you can use the class method respond_to
+ # with the respond_with method to have the same results:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def index
+ # @people = Person.find(:all)
+ # respond_with(@person)
+ # end
+ # end
+ #
+ # Be sure to check respond_with and respond_to documentation for more examples.
+ #
+ def respond_to(*mimes, &block)
+ options = mimes.extract_options!
+ raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
- class Responder #:nodoc:
-
- def initialize(controller)
- @controller = controller
- @request = controller.request
- @response = controller.response
+ resource = options.delete(:with)
+ responder = Responder.new
- @mime_type_priority = @request.formats
+ mimes = collect_mimes_from_class_level if mimes.empty?
+ mimes.each { |mime| responder.send(mime) }
+ block.call(responder) if block_given?
- @order = []
- @responses = {}
+ if format = request.negotiate_mime(responder.order)
+ respond_to_block_or_template_or_resource(format, resource,
+ options, &responder.response_for(format))
+ else
+ head :not_acceptable
end
+ end
- def custom(mime_type, &block)
- mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
+ # respond_with allows you to respond an action with a given resource. It
+ # requires that you set your class with a :respond_to method with the
+ # formats allowed:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def index
+ # @people = Person.find(:all)
+ # respond_with(@person)
+ # end
+ # end
+ #
+ # When a request comes with format :xml, the respond_with will first search
+ # for a template as person/index.xml, if the template is not available, it
+ # will see if the given resource responds to :to_xml.
+ #
+ # If neither are available, it will raise an error.
+ #
+ # Extra parameters given to respond_with are used when :to_format is invoked.
+ # This allows you to set status and location for several formats at the same
+ # time. Consider this restful controller response on create for both xml
+ # and json formats:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :xml, :json
+ #
+ # def create
+ # @person = Person.new(params[:person])
+ #
+ # if @person.save
+ # respond_with(@person, :status => :ok, :location => person_url(@person))
+ # else
+ # respond_with(@person.errors, :status => :unprocessable_entity)
+ # end
+ # end
+ # end
+ #
+ # Finally, respond_with also accepts blocks, as in respond_to. Let's take
+ # the same controller and create action above and add common html behavior:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def create
+ # @person = Person.new(params[:person])
+ #
+ # if @person.save
+ # options = { :status => :ok, :location => person_url(@person) }
+ #
+ # respond_with(@person, options) do |format|
+ # format.html { redirect_to options[:location] }
+ # end
+ # else
+ # respond_with(@person.errors, :status => :unprocessable_entity) do
+ # format.html { render :action => :new }
+ # end
+ # end
+ # end
+ # end
+ #
+ def respond_with(resource, options={}, &block)
+ respond_to(options.merge!(:with => resource), &block)
+ end
- @order << mime_type
+ protected
- @responses[mime_type] ||= Proc.new do
- # TODO: Remove this when new base is merged in
- @controller.formats = [mime_type.to_sym]
- @controller.content_type = mime_type
- @controller.template.formats = [mime_type.to_sym]
+ def respond_to_block_or_template_or_resource(format, resource, options)
+ self.formats = [format.to_sym]
+ return yield if block_given?
- block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
+ begin
+ default_render
+ rescue ActionView::MissingTemplate => e
+ if resource && resource.respond_to?(:"to_#{format.to_sym}")
+ render options.merge(format.to_sym => resource)
+ else
+ raise e
end
end
+ end
+
+ # Collect mimes declared in the class method respond_to valid for the
+ # current action.
+ #
+ def collect_mimes_from_class_level #:nodoc:
+ action = action_name.to_sym
+
+ mimes_for_respond_to.keys.select do |mime|
+ config = mimes_for_respond_to[mime]
+
+ if config[:except]
+ !config[:except].include?(action)
+ elsif config[:only]
+ config[:only].include?(action)
+ else
+ true
+ end
+ end
+ end
+
+ class Responder #:nodoc:
+ attr_accessor :order
+
+ def initialize
+ @order, @responses = [], {}
+ end
def any(*args, &block)
if args.any?
args.each { |type| send(type, &block) }
else
- custom(@mime_type_priority.first, &block)
+ custom(Mime::ALL, &block)
end
end
-
+ alias :all :any
+
+ def custom(mime_type, &block)
+ mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
+
+ @order << mime_type
+ @responses[mime_type] ||= block
+ end
+
+ def response_for(mime)
+ @responses[mime] || @responses[Mime::ALL]
+ end
+
def self.generate_method_for_mime(mime)
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
const = sym.to_s.upcase
@@ -152,7 +339,7 @@ module ActionController #:nodoc:
def method_missing(symbol, &block)
mime_constant = Mime.const_get(symbol.to_s.upcase)
-
+
if Mime::SET.include?(mime_constant)
self.class.generate_method_for_mime(mime_constant)
send(symbol, &block)
@@ -161,25 +348,6 @@ module ActionController #:nodoc:
end
end
- def respond
- for priority in @mime_type_priority
- if priority == Mime::ALL
- @responses[@order.first].call
- return
- else
- if @responses[priority]
- @responses[priority].call
- return # mime type match found, be happy and return
- end
- end
- end
-
- if @order.include?(Mime::ALL)
- @responses[Mime::ALL].call
- else
- @controller.send :head, :not_acceptable
- end
- end
end
end
end
diff --git a/actionpack/lib/action_controller/base/renderer.rb b/actionpack/lib/action_controller/base/renderer.rb
index 2fab501302..572da451ff 100644
--- a/actionpack/lib/action_controller/base/renderer.rb
+++ b/actionpack/lib/action_controller/base/renderer.rb
@@ -11,11 +11,10 @@ module ActionController
def render(options)
super
- options[:_template] ||= _action_view._partial
self.content_type ||= begin
mime = options[:_template].mime_type
formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first)
- end
+ end.to_s
response_body
end
diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb
index 6ba86cd0be..ad06657f86 100644
--- a/actionpack/lib/action_controller/base/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/base/request_forgery_protection.rb
@@ -106,8 +106,7 @@ module ActionController #:nodoc:
!request.content_type.nil? && request.content_type.verify_request?
end
- # Sets the token value for the current session. Pass a <tt>:secret</tt> option
- # in +protect_from_forgery+ to add a custom salt to the hash.
+ # Sets the token value for the current session.
def form_authenticity_token
session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
end
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
index ce59866531..5b9ded83dd 100644
--- a/actionpack/lib/action_controller/routing.rb
+++ b/actionpack/lib/action_controller/routing.rb
@@ -139,7 +139,7 @@ module ActionController
# # In routes.rb
# map.with_options :controller => 'blog' do |blog|
# blog.show '', :action => 'list'
- # blog.delete 'delete/:id', :action => 'delete',
+ # blog.delete 'delete/:id', :action => 'delete'
# blog.edit 'edit/:id', :action => 'edit'
# end
#
diff --git a/actionpack/lib/action_controller/routing/generation/url_rewriter.rb b/actionpack/lib/action_controller/routing/generation/url_rewriter.rb
index 16720b915b..9717582b5e 100644
--- a/actionpack/lib/action_controller/routing/generation/url_rewriter.rb
+++ b/actionpack/lib/action_controller/routing/generation/url_rewriter.rb
@@ -93,7 +93,7 @@ module ActionController
#
# * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targetted at.
+ # * <tt>:host</tt> - Specifies the host the link should be targeted at.
# If <tt>:only_path</tt> is false, this option must be
# provided either explicitly, or via +default_url_options+.
# * <tt>:port</tt> - Optionally specify the port to connect to.
diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb
index f5a4b1e1db..040a7e2cb6 100644
--- a/actionpack/lib/action_controller/routing/route_set.rb
+++ b/actionpack/lib/action_controller/routing/route_set.rb
@@ -155,7 +155,7 @@ module ActionController
def define_url_helper(route, name, kind, options)
selector = url_helper_name(name, kind)
- # The segment keys used for positional paramters
+ # The segment keys used for positional parameters
hash_access_method = hash_access_name(name, kind)
diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb
index 7b4eda58e5..a11755b517 100644
--- a/actionpack/lib/action_controller/testing/test_case.rb
+++ b/actionpack/lib/action_controller/testing/test_case.rb
@@ -56,7 +56,7 @@ module ActionController
#
# ActionController::TestCase will automatically infer the controller under test
# from the test class name. If the controller cannot be inferred from the test
- # class name, you can explicity set it with +tests+.
+ # class name, you can explicitly set it with +tests+.
#
# class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
# tests WidgetController
@@ -182,7 +182,7 @@ module ActionController
@controller.send(:initialize_current_url)
end
end
-
+
# Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
def rescue_action_in_public!
@request.remote_addr = '208.77.188.166' # example.com
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 27f27e27fe..cc989d6625 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -84,7 +84,7 @@ module Mime
end
def lookup_by_extension(extension)
- EXTENSION_LOOKUP[extension]
+ EXTENSION_LOOKUP[extension.to_s]
end
# Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 7c28cac419..68f37d2f65 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -2,7 +2,6 @@
# http://www.iana.org/assignments/media-types/
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
-Mime::Type.register "*/*", :all
Mime::Type.register "text/plain", :text, [], %w(txt)
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
Mime::Type.register "text/css", :css
@@ -18,4 +17,7 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
# http://www.ietf.org/rfc/rfc4627.txt
# http://www.json.org/JSONRequest.html
-Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest ) \ No newline at end of file
+Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
+
+# Create Mime::ALL but do not add it to the SET.
+Mime::ALL = Mime::Type.new("*/*", :all, [])
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 3f23a5af7a..5f9463eb91 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -161,7 +161,7 @@ module ActionDispatch
# 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 = [])
@env["action_dispatch.request.format"] ||=
if parameters[:format]
@@ -173,13 +173,11 @@ module ActionDispatch
end
end
+ # Expand raw_formats by converting Mime::ALL to the Mime::SET.
+ #
def formats
if ActionController::Base.use_accept_header
- if param = parameters[:format]
- Array.wrap(Mime[param])
- else
- accepts.dup
- end.tap do |ret|
+ raw_formats.tap do |ret|
if ret == ONLY_ALL
ret.replace Mime::SET
elsif all = ret.index(Mime::ALL)
@@ -187,7 +185,7 @@ module ActionDispatch
end
end
else
- [format] + Mime::SET
+ raw_formats + Mime::SET
end
end
@@ -232,7 +230,7 @@ module ActionDispatch
def xml_http_request?
!(@env['HTTP_X_REQUESTED_WITH'] !~ /XMLHttpRequest/i)
end
- alias xhr? :xml_http_request?
+ alias :xhr? :xml_http_request?
# Which IP addresses are "trusted proxies" that can be stripped from
# the right-hand-side of X-Forwarded-For
@@ -485,7 +483,35 @@ EOM
session['flash'] || {}
end
+ # Receives an array of mimes and return the first user sent mime that
+ # matches the order array.
+ #
+ def negotiate_mime(order)
+ raw_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 raw_formats
+ if ActionController::Base.use_accept_header
+ if param = parameters[:format]
+ Array.wrap(Mime[param])
+ else
+ accepts.dup
+ end
+ else
+ [format]
+ end
+ end
+
def named_host?(host)
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
end
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index 0f122d9232..4fd7f7d83c 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -160,11 +160,24 @@ module ActionView
#
# error_messages_for 'user'
#
+ # You can also supply an object:
+ #
+ # error_messages_for @user
+ #
+ # This will use the last part of the model name in the presentation. For instance, if
+ # this is a MyKlass::User object, this will use "user" as the name in the String. This
+ # is taken from MyKlass::User.model_name.human, which can be overridden.
+ #
# To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
# will be the name used in the header message:
#
# error_messages_for 'user_common', 'user', :object_name => 'user'
#
+ # You can also use a number of objects, which will have the same naming semantics
+ # as a single object.
+ #
+ # error_messages_for @user, @post
+ #
# If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
# object (or array of objects to use):
#
@@ -176,15 +189,20 @@ module ActionView
def error_messages_for(*params)
options = params.extract_options!.symbolize_keys
- if object = options.delete(:object)
- objects = [object].flatten
- else
- objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
+ objects = Array.wrap(options.delete(:object) || params).map do |object|
+ unless object.respond_to?(:to_model)
+ object = instance_variable_get("@#{object}")
+ object = convert_to_model(object)
+ else
+ object = object.to_model
+ options[:object_name] ||= object.class.model_name.human
+ end
+ object
end
- objects.map! {|o| convert_to_model(o) }
+ objects.compact!
- count = objects.inject(0) {|sum, object| sum + object.errors.count }
+ count = objects.inject(0) {|sum, object| sum + object.errors.count }
unless count.zero?
html = {}
[:id, :class].each do |key|
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 3fde79dfa4..081003bcf3 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -557,7 +557,7 @@ module ActionView
# video_tag("trailer.ogg") # =>
# <video src="/videos/trailer.ogg" />
# video_tag("trailer.ogg", :controls => true, :autobuffer => true) # =>
- # <video autobuffer="true" controls="true" src="/videos/trailer.ogg" />
+ # <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
# video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # =>
# <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png" />
# video_tag("/trailers/hd.avi", :size => "16x16") # =>
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 72fe9a3232..332743d55b 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -112,12 +112,12 @@ module ActionView
# ==== Options
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
# "2" instead of "February").
- # * <tt>:use_short_month</tt> - Set to true if you want to use the abbreviated month name instead of the full
- # name (e.g. "Feb" instead of "February").
- # * <tt>:add_month_number</tt> - Set to true if you want to show both, the month's number and name (e.g.
+ # * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
+ # month names (e.g. "Feb" instead of "February").
+ # * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
# "2 - February" instead of "February").
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
- # Note: You can also use Rails' new i18n functionality for this.
+ # Note: You can also use Rails' i18n functionality for this.
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
@@ -128,7 +128,7 @@ module ActionView
# as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
# * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
# as a hidden field instead of showing a select field.
- # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> do
+ # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
# customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
# select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in
# the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 6adbab175f..8cb5882aab 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -167,31 +167,31 @@ module ActionView
#
# In addition to the <tt>:include_blank</tt> option documented above,
# this method also supports a <tt>:model</tt> option, which defaults
- # to TimeZone. This may be used by users to specify a different time
- # zone model object. (See +time_zone_options_for_select+ for more
- # information.)
+ # to ActiveSupport::TimeZone. This may be used by users to specify a
+ # different time zone model object. (See +time_zone_options_for_select+
+ # for more information.)
#
- # You can also supply an array of TimeZone objects
+ # You can also supply an array of ActiveSupport::TimeZone objects
# as +priority_zones+, so that they will be listed above the rest of the
- # (long) list. (You can use TimeZone.us_zones as a convenience for
- # obtaining a list of the US time zones, or a Regexp to select the zones
+ # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
+ # for obtaining a list of the US time zones, or a Regexp to select the zones
# of your choice)
#
# Finally, this method supports a <tt>:default</tt> option, which selects
- # a default TimeZone if the object's time zone is +nil+.
+ # a default ActiveSupport::TimeZone if the object's time zone is +nil+.
#
# Examples:
# time_zone_select( "user", "time_zone", nil, :include_blank => true)
#
# time_zone_select( "user", "time_zone", nil, :default => "Pacific Time (US & Canada)" )
#
- # time_zone_select( "user", 'time_zone', TimeZone.us_zones, :default => "Pacific Time (US & Canada)")
+ # time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, :default => "Pacific Time (US & Canada)")
#
- # time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ])
+ # time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
#
# time_zone_select( "user", 'time_zone', /Australia/)
#
- # time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone)
+ # time_zone_select( "user", "time_zone", ActiveSupport::Timezone.all.sort, :model => ActiveSupport::Timezone)
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
InstanceTag.new(object, method, self, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
end
@@ -393,20 +393,20 @@ module ActionView
end
# Returns a string of option tags for pretty much any time zone in the
- # world. Supply a TimeZone name as +selected+ to have it marked as the
- # selected option tag. You can also supply an array of TimeZone objects
- # as +priority_zones+, so that they will be listed above the rest of the
- # (long) list. (You can use TimeZone.us_zones as a convenience for
- # obtaining a list of the US time zones, or a Regexp to select the zones
- # of your choice)
+ # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
+ # marked as the selected option tag. You can also supply an array of
+ # ActiveSupport::TimeZone objects as +priority_zones+, so that they will
+ # be listed above the rest of the (long) list. (You can use
+ # ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list
+ # of the US time zones, or a Regexp to select the zones of your choice)
#
# The +selected+ parameter must be either +nil+, or a string that names
- # a TimeZone.
+ # a ActiveSupport::TimeZone.
#
- # By default, +model+ is the TimeZone constant (which can be obtained
- # in Active Record as a value object). The only requirement is that the
- # +model+ parameter be an object that responds to +all+, and returns
- # an array of objects that represent time zones.
+ # By default, +model+ is the ActiveSupport::TimeZone constant (which can
+ # be obtained in Active Record as a value object). The only requirement
+ # is that the +model+ parameter be an object that responds to +all+, and
+ # returns an array of objects that represent time zones.
#
# NOTE: Only the option tags are returned, you have to wrap this call in
# a regular HTML select tag.
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 66d7592874..eea797abb5 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -8,7 +8,10 @@ module ActionView
module TagHelper
include ERB::Util
- BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set
+ BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
+ autoplay controls loop selected hidden scoped async
+ defer reversed ismap seemless muted required
+ autofocus novalidate formnovalidate open).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attr| attr.to_sym })
# Returns an empty HTML tag of type +name+ which by default is XHTML
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index ad0733a7e1..c3ce4c671e 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -436,7 +436,7 @@ module ActionView
end
# Returns the current cycle string after a cycle has been started. Useful
- # for complex table highlighing or any other design need which requires
+ # for complex table highlighting or any other design need which requires
# the current cycle string in more than one place.
#
# ==== Example
@@ -544,7 +544,7 @@ module ActionView
left, right = $`, $'
# detect already linked URLs and URLs in the middle of a tag
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
- # do not change string; URL is alreay linked
+ # do not change string; URL is already linked
href
else
# don't include trailing punctuation character as part of the URL
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index de864f453c..c5a6d1f084 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -12,11 +12,11 @@ module ActionView
# Returns the URL for the set of +options+ provided. This takes the
# same options as +url_for+ in Action Controller (see the
- # documentation for ActionController::Base#url_for). Note that by default
- # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative /controller/action
- # instead of the fully qualified URL like http://example.com/controller/action.
+ # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
+ # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
+ # instead of the fully qualified URL like "http://example.com/controller/action".
#
- # When called from a view, url_for returns an HTML escaped url. If you
+ # When called from a view, +url_for+ returns an HTML escaped url. If you
# need an unescaped url, pass <tt>:escape => false</tt> in the +options+.
#
# ==== Options
@@ -34,8 +34,8 @@ module ActionView
#
# If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter,
# you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing
- # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as
- # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route).
+ # a Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
+ # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
#
# ==== Examples
# <%= url_for(:action => 'index') %>
@@ -97,10 +97,10 @@ module ActionView
# Creates a link tag of the given +name+ using a URL created by the set
# of +options+. See the valid options in the documentation for
- # url_for. It's also possible to pass a string instead
+ # +url_for+. It's also possible to pass a string instead
# of an options hash to get a link tag that uses the value of the string as the
# href for the link, or use <tt>:back</tt> to link to the referrer - a JavaScript back
- # link will be used in place of a referrer if none exists. If nil is passed as
+ # link will be used in place of a referrer if none exists. If +nil+ is passed as
# a name, the link itself will become the name.
#
# ==== Signatures
@@ -117,27 +117,22 @@ module ActionView
# * <tt>:popup => true || array of window options</tt> - This will force the
# link to open in a popup window. By passing true, a default browser window
# will be opened with the URL. You can also specify an array of options
- # that are passed-thru to JavaScripts window.open method.
+ # that are passed to the <tt>window.open</tt> JavaScript call.
# * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
# create an HTML form and immediately submit the form for processing using
# the HTTP verb specified. Useful for having links perform a POST operation
# in dangerous actions like deleting a record (which search bots can follow
# while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>.
# Note that if the user has JavaScript disabled, the request will fall back
- # to using GET. If you are relying on the POST behavior, you should check
- # for it in your controller's action by using the request object's methods
- # for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
+ # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript
+ # disabled clicking the link will have no effect. If you are relying on the
+ # POST behavior, you should check for it in your controller's action by using
+ # the request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
# * The +html_options+ will accept a hash of html attributes for the link tag.
#
- # Note that if the user has JavaScript disabled, the request will fall back
- # to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript disabled
- # clicking the link will have no effect. If you are relying on the POST
- # behavior, your should check for it in your controller's action by using the
- # request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
- #
# You can mix and match the +html_options+ with the exception of
- # <tt>:popup</tt> and <tt>:method</tt> which will raise an ActionView::ActionViewError
- # exception.
+ # <tt>:popup</tt> and <tt>:method</tt> which will raise an
+ # <tt>ActionView::ActionViewError</tt> exception.
#
# ==== Examples
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
@@ -170,9 +165,11 @@ module ActionView
# You can use a block as well if your link target is hard to fit into the name parameter. ERb example:
#
# <% link_to(@profile) do %>
- # <strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
+ # <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
# <% end %>
- # # => <a href="/profiles/1"><strong>David</strong> -- <span>Check it out!!</span></a>
+ # # => <a href="/profiles/1">
+ # <strong>David</strong> -- <span>Check it out!</span>
+ # </a>
#
# Classes and ids for CSS are easy to produce:
#
@@ -215,7 +212,9 @@ module ActionView
# # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
# f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
# var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
- # m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;">Delete Image</a>
+ # m.setAttribute('value', 'delete');var s = document.createElement('input'); s.setAttribute('type', 'hidden');
+ # s.setAttribute('name', 'authenticity_token'); s.setAttribute('value', 'Q/ttlxPYZ6R77B+vZ1sBkhj21G2isO9dpE6UtOHBApg=');
+ # f.appendChild(s)f.appendChild(m);f.submit(); };return false;">Delete Image</a>
def link_to(*args, &block)
if block_given?
options = args.first || {}
@@ -246,21 +245,21 @@ module ActionView
# by the set of +options+. This is the safest method to ensure links that
# cause changes to your data are not triggered by search bots or accelerators.
# If the HTML button does not work with your layout, you can also consider
- # using the link_to method with the <tt>:method</tt> modifier as described in
- # the link_to documentation.
+ # using the +link_to+ method with the <tt>:method</tt> modifier as described in
+ # the +link_to+ documentation.
#
- # The generated FORM element has a class name of <tt>button-to</tt>
+ # The generated form element has a class name of <tt>button-to</tt>
# to allow styling of the form itself and its children. You can control
# the form submission and input element behavior using +html_options+.
# This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
- # described in the link_to documentation. If no <tt>:method</tt> modifier
+ # described in the +link_to+ documentation. If no <tt>:method</tt> modifier
# is given, it will default to performing a POST operation. You can also
# disable the button by passing <tt>:disabled => true</tt> in +html_options+.
# If you are using RESTful routes, you can pass the <tt>:method</tt>
# to change the HTTP verb used to submit the form.
#
# ==== Options
- # The +options+ hash accepts the same options at url_for.
+ # The +options+ hash accepts the same options as url_for.
#
# There are a few special +html_options+:
# * <tt>:method</tt> - Specifies the anchor name to be appended to the path.
@@ -317,7 +316,7 @@ module ActionView
# Creates a link tag of the given +name+ using a URL created by the set of
# +options+ unless the current request URI is the same as the links, in
# which case only the name is returned (or the given block is yielded, if
- # one exists). You can give link_to_unless_current a block which will
+ # one exists). You can give +link_to_unless_current+ a block which will
# specialize the default behavior (e.g., show a "Start Here" link rather
# than the link's text).
#
@@ -343,7 +342,7 @@ module ActionView
# <li><a href="/controller/about">About Us</a></li>
# </ul>
#
- # The implicit block given to link_to_unless_current is evaluated if the current
+ # The implicit block given to +link_to_unless_current+ is evaluated if the current
# action is the action given. So, if we had a comments page and wanted to render a
# "Go Back" link instead of a link to the comments page, we could do something like this...
#
@@ -360,7 +359,7 @@ module ActionView
# +options+ unless +condition+ is true, in which case only the name is
# returned. To specialize the default behavior (i.e., show a login link rather
# than just the plaintext link text), you can pass a block that
- # accepts the name or the full argument list for link_to_unless.
+ # accepts the name or the full argument list for +link_to_unless+.
#
# ==== Examples
# <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
@@ -391,8 +390,8 @@ module ActionView
# Creates a link tag of the given +name+ using a URL created by the set of
# +options+ if +condition+ is true, in which case only the name is
# returned. To specialize the default behavior, you can pass a block that
- # accepts the name or the full argument list for link_to_unless (see the examples
- # in link_to_unless).
+ # accepts the name or the full argument list for +link_to_unless+ (see the examples
+ # in +link_to_unless+).
#
# ==== Examples
# <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %>
@@ -416,27 +415,27 @@ module ActionView
# also used as the name of the link unless +name+ is specified. Additional
# HTML attributes for the link can be passed in +html_options+.
#
- # mail_to has several methods for hindering email harvesters and customizing
+ # +mail_to+ has several methods for hindering email harvesters and customizing
# the email itself by passing special keys to +html_options+.
#
# ==== Options
- # * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex".
- # Passing "javascript" will dynamically create and encode the mailto: link then
+ # * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex".
+ # Passing "javascript" will dynamically create and encode the mailto link then
# eval it into the DOM of the page. This method will not show the link on
# the page if the user has JavaScript disabled. Passing "hex" will hex
- # encode the +email_address+ before outputting the mailto: link.
- # * <tt>:replace_at</tt> - When the link +name+ isn't provided, the
+ # encode the +email_address+ before outputting the mailto link.
+ # * <tt>:replace_at</tt> - When the link +name+ isn't provided, the
# +email_address+ is used for the link label. You can use this option to
# obfuscate the +email_address+ by substituting the @ sign with the string
# given as the value.
- # * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the
+ # * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the
# +email_address+ is used for the link label. You can use this option to
# obfuscate the +email_address+ by substituting the . in the email with the
# string given as the value.
- # * <tt>:subject</tt> - Preset the subject line of the email.
+ # * <tt>:subject</tt> - Preset the subject line of the email.
# * <tt>:body</tt> - Preset the body of the email.
- # * <tt>:cc</tt> - Carbon Copy addition recipients on the email.
- # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
+ # * <tt>:cc</tt> - Carbon Copy addition recipients on the email.
+ # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
#
# ==== Examples
# mail_to "me@domain.com"
@@ -607,23 +606,23 @@ module ActionView
submit_function << "f.submit();"
end
- # Processes the _html_options_ hash, converting the boolean
+ # Processes the +html_options+ hash, converting the boolean
# attributes from true/false form into the form required by
# HTML/XHTML. (An attribute is considered to be boolean if
- # its name is listed in the given _bool_attrs_ array.)
+ # its name is listed in the given +bool_attrs+ array.)
#
- # More specifically, for each boolean attribute in _html_options_
+ # More specifically, for each boolean attribute in +html_options+
# given as:
#
- # "attr" => bool_value
+ # "attr" => bool_value
#
- # if the associated _bool_value_ evaluates to true, it is
+ # if the associated +bool_value+ evaluates to true, it is
# replaced with the attribute's name; otherwise the attribute is
- # removed from the _html_options_ hash. (See the XHTML 1.0 spec,
+ # removed from the +html_options+ hash. (See the XHTML 1.0 spec,
# section 4.5 "Attribute Minimization" for more:
# http://www.w3.org/TR/xhtml1/#h-4.5)
#
- # Returns the updated _html_options_ hash, which is also modified
+ # Returns the updated +html_options+ hash, which is also modified
# in place.
#
# Example:
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 93ca34c41c..117f4ea4f0 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -86,6 +86,7 @@ class RespondToController < ActionController::Base
type.mobile { render :text => "Mobile" }
end
ensure
+ Mime::SET.delete(:mobile)
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
end
@@ -98,6 +99,7 @@ class RespondToController < ActionController::Base
end
ensure
+ Mime::SET.delete(:mobile)
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
end
@@ -132,6 +134,7 @@ class RespondToController < ActionController::Base
end
ensure
+ Mime::SET.delete(:iphone)
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
@@ -145,6 +148,7 @@ class RespondToController < ActionController::Base
end
ensure
+ Mime::SET.delete(:iphone)
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
@@ -162,7 +166,7 @@ class RespondToController < ActionController::Base
end
end
-class MimeControllerTest < ActionController::TestCase
+class RespondToControllerTest < ActionController::TestCase
tests RespondToController
def setup
@@ -436,10 +440,10 @@ class MimeControllerTest < ActionController::TestCase
def test_render_action_for_html
@controller.instance_eval do
def render(*args)
- unless args.empty?
- @action = args.first[:action] || action_name
- end
- response.body = "#{@action} - #{@template.formats}"
+ @action = args.first[:action] unless args.empty?
+ @action ||= action_name
+
+ response.body = "#{@action} - #{formats}"
end
end
@@ -467,7 +471,185 @@ class MimeControllerTest < ActionController::TestCase
end
end
+class RespondResource
+ undef_method :to_json
+
+ def to_xml
+ "XML"
+ end
+
+ def to_js
+ "JS"
+ end
+end
+
+class RespondWithController < ActionController::Base
+ respond_to :html, :json
+ respond_to :xml, :except => :using_defaults
+ respond_to :js, :only => :using_defaults
+
+ def using_defaults
+ respond_to do |format|
+ format.csv { render :text => "CSV" }
+ end
+ end
+
+ def using_defaults_with_type_list
+ respond_to(:js, :xml)
+ end
+
+ def using_resource
+ respond_with(RespondResource.new)
+ end
+
+ def using_resource_with_options
+ respond_with(RespondResource.new, :status => :unprocessable_entity) do |format|
+ format.js
+ end
+ end
+
+ def default_overwritten
+ respond_to do |format|
+ format.html { render :text => "HTML" }
+ end
+ end
+
+protected
+
+ def _render_js(js, options)
+ self.content_type ||= Mime::JS
+ self.response_body = js.respond_to?(:to_js) ? js.to_js : js
+ end
+end
+
+class InheritedRespondWithController < RespondWithController
+ clear_respond_to
+ respond_to :xml, :json
+
+ def index
+ respond_with(RespondResource.new) do |format|
+ format.json { render :text => "JSON" }
+ end
+ end
+end
+
+class RespondWithControllerTest < ActionController::TestCase
+ tests RespondWithController
+
+ def setup
+ super
+ ActionController::Base.use_accept_header = true
+ @request.host = "www.example.com"
+ end
+
+ def teardown
+ super
+ ActionController::Base.use_accept_header = false
+ end
+
+ def test_using_defaults
+ @request.accept = "*/*"
+ get :using_defaults
+ assert_equal "text/html", @response.content_type
+ assert_equal 'Hello world!', @response.body
+
+ @request.accept = "text/csv"
+ get :using_defaults
+ assert_equal "text/csv", @response.content_type
+ assert_equal "CSV", @response.body
+
+ @request.accept = "text/javascript"
+ get :using_defaults
+ assert_equal "text/javascript", @response.content_type
+ assert_equal '$("body").visualEffect("highlight");', @response.body
+ end
+
+ def test_using_defaults_with_type_list
+ @request.accept = "*/*"
+ get :using_defaults_with_type_list
+ assert_equal "text/javascript", @response.content_type
+ assert_equal '$("body").visualEffect("highlight");', @response.body
+
+ @request.accept = "application/xml"
+ get :using_defaults_with_type_list
+ assert_equal "application/xml", @response.content_type
+ assert_equal "<p>Hello world!</p>\n", @response.body
+ end
+
+ def test_using_resource
+ @request.accept = "text/html"
+ get :using_resource
+ assert_equal "text/html", @response.content_type
+ assert_equal "Hello world!", @response.body
+
+ @request.accept = "application/xml"
+ get :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal "XML", @response.body
+
+ @request.accept = "application/json"
+ assert_raise ActionView::MissingTemplate do
+ get :using_resource
+ end
+ end
+
+ def test_using_resource_with_options
+ @request.accept = "application/xml"
+ get :using_resource_with_options
+ assert_equal "application/xml", @response.content_type
+ assert_equal 422, @response.status
+ assert_equal "XML", @response.body
+
+ @request.accept = "text/javascript"
+ get :using_resource_with_options
+ assert_equal "text/javascript", @response.content_type
+ assert_equal 422, @response.status
+ assert_equal "JS", @response.body
+ end
+
+ def test_default_overwritten
+ get :default_overwritten
+ assert_equal "text/html", @response.content_type
+ assert_equal "HTML", @response.body
+ end
+
+ def test_clear_respond_to
+ @controller = InheritedRespondWithController.new
+ @request.accept = "text/html"
+ get :index
+ assert_equal 406, @response.status
+ end
+
+ def test_first_in_respond_to_has_higher_priority
+ @controller = InheritedRespondWithController.new
+ @request.accept = "*/*"
+ get :index
+ assert_equal "application/xml", @response.content_type
+ assert_equal "XML", @response.body
+ end
+
+ def test_not_acceptable
+ @request.accept = "application/xml"
+ get :using_defaults
+ assert_equal 406, @response.status
+
+ @request.accept = "text/html"
+ get :using_defaults_with_type_list
+ assert_equal 406, @response.status
+
+ @request.accept = "application/json"
+ get :using_defaults_with_type_list
+ assert_equal 406, @response.status
+
+ @request.accept = "text/javascript"
+ get :using_resource
+ assert_equal 406, @response.status
+ end
+end
+
class AbstractPostController < ActionController::Base
+ respond_to :html, :iphone
+
self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/"
end
@@ -476,10 +658,7 @@ class PostController < AbstractPostController
around_filter :with_iphone
def index
- respond_to do |type|
- type.html
- type.iphone
- end
+ respond_to # It will use formats declared above
end
protected
@@ -489,17 +668,12 @@ protected
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
yield
ensure
+ Mime::SET.delete(:iphone)
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
end
class SuperPostController < PostController
- def index
- respond_to do |type|
- type.html
- type.iphone
- end
- end
end
class MimeControllerLayoutsTest < ActionController::TestCase
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 3a85db8aa5..8ebf9aa186 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -338,16 +338,11 @@ class RequestTest < ActiveSupport::TestCase
end
test "XMLHttpRequest" do
- begin
- ActionController::Base.use_accept_header, old =
- false, ActionController::Base.use_accept_header
-
+ with_accept_header false do
request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'
request.expects(:parameters).at_least_once.returns({})
assert request.xhr?
assert_equal Mime::JS, request.format
- ensure
- ActionController::Base.use_accept_header = old
end
end
@@ -396,10 +391,54 @@ class RequestTest < ActiveSupport::TestCase
assert_equal({"bar" => 2}, request.query_parameters)
end
+ test "formats with accept header" do
+ with_accept_header true do
+ request = stub_request 'HTTP_ACCEPT' => 'text/html'
+ request.expects(:parameters).at_least_once.returns({})
+ assert_equal [ Mime::HTML ], request.formats
+
+ request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
+ request.expects(:parameters).at_least_once.returns({})
+ assert_equal with_set(Mime::XML, Mime::HTML), request.formats
+ end
+
+ with_accept_header false do
+ request = stub_request
+ request.expects(:parameters).at_least_once.returns({ :format => :txt })
+ assert_equal with_set(Mime::TEXT), request.formats
+ end
+ end
+
+ test "negotiate_mime" do
+ with_accept_header true do
+ request = stub_request 'HTTP_ACCEPT' => 'text/html'
+ request.expects(:parameters).at_least_once.returns({})
+
+ assert_equal nil, request.negotiate_mime([Mime::XML, Mime::JSON])
+ assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::HTML])
+ assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::ALL])
+
+ request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
+ request.expects(:parameters).at_least_once.returns({})
+ assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV])
+ assert_equal Mime::CSV, request.negotiate_mime([Mime::CSV, Mime::YAML])
+ end
+ end
+
protected
def stub_request(env={})
ActionDispatch::Request.new(env)
end
+ def with_set(*args)
+ args + Mime::SET
+ end
+
+ def with_accept_header(value)
+ ActionController::Base.use_accept_header, old = value, ActionController::Base.use_accept_header
+ yield
+ ensure
+ ActionController::Base.use_accept_header = old
+ end
end
diff --git a/actionpack/test/fixtures/public/.gitignore b/actionpack/test/fixtures/public/.gitignore
index 0c6759baec..312e635ee6 100644
--- a/actionpack/test/fixtures/public/.gitignore
+++ b/actionpack/test/fixtures/public/.gitignore
@@ -1 +1 @@
-absolute \ No newline at end of file
+absolute/*
diff --git a/actionpack/test/fixtures/respond_with/using_defaults.html.erb b/actionpack/test/fixtures/respond_with/using_defaults.html.erb
new file mode 100644
index 0000000000..6769dd60bd
--- /dev/null
+++ b/actionpack/test/fixtures/respond_with/using_defaults.html.erb
@@ -0,0 +1 @@
+Hello world! \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_defaults.js.rjs b/actionpack/test/fixtures/respond_with/using_defaults.js.rjs
new file mode 100644
index 0000000000..469fcd8e15
--- /dev/null
+++ b/actionpack/test/fixtures/respond_with/using_defaults.js.rjs
@@ -0,0 +1 @@
+page[:body].visual_effect :highlight \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs b/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs
new file mode 100644
index 0000000000..469fcd8e15
--- /dev/null
+++ b/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs
@@ -0,0 +1 @@
+page[:body].visual_effect :highlight \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder b/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder
new file mode 100644
index 0000000000..598d62e2fc
--- /dev/null
+++ b/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder
@@ -0,0 +1 @@
+xml.p "Hello world!" \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_resource.html.erb b/actionpack/test/fixtures/respond_with/using_resource.html.erb
new file mode 100644
index 0000000000..6769dd60bd
--- /dev/null
+++ b/actionpack/test/fixtures/respond_with/using_resource.html.erb
@@ -0,0 +1 @@
+Hello world! \ No newline at end of file
diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb
index 63032e4e5c..047f81be29 100644
--- a/actionpack/test/template/active_record_helper_i18n_test.rb
+++ b/actionpack/test/template/active_record_helper_i18n_test.rb
@@ -3,11 +3,14 @@ require 'abstract_unit'
class ActiveRecordHelperI18nTest < Test::Unit::TestCase
include ActionView::Context
include ActionView::Helpers::ActiveModelHelper
-
+
attr_reader :request
def setup
@object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
+ @object.stubs :to_model => @object
+ @object.stubs :class => stub(:model_name => stub(:human => ""))
+
@object_name = 'book_seller'
@object_name_without_underscore = 'book seller'
@@ -39,7 +42,7 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase
I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
error_messages_for(:object => @object, :locale => 'en')
end
-
+
def test_error_messages_for_given_object_name_it_translates_object_name
I18n.expects(:t).with(:header, :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => @object_name_without_underscore).returns "1 error prohibited this #{@object_name_without_underscore} from being saved"
I18n.expects(:t).with(@object_name, :default => @object_name_without_underscore, :count => 1, :scope => [:activerecord, :models]).once.returns @object_name_without_underscore
diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb
index b07ce6cf5d..ec3384f15d 100644
--- a/actionpack/test/template/active_record_helper_test.rb
+++ b/actionpack/test/template/active_record_helper_test.rb
@@ -295,6 +295,16 @@ class ActiveRecordHelperTest < ActionView::TestCase
assert_equal '', error_messages_for('user', :object => nil)
end
+ def test_error_messages_for_model_objects
+ error = error_messages_for(@post)
+ assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>),
+ error
+
+ error = error_messages_for(@user, @post)
+ assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>),
+ error
+ end
+
def test_form_with_string_multipart
assert_dom_equal(
%(<form action="create" enctype="multipart/form-data" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 921bfeb93a..08963946ab 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -158,8 +158,8 @@ class AssetTagHelperTest < ActionView::TestCase
VideoLinkToTag = {
%(video_tag("xml.ogg")) => %(<video src="/videos/xml.ogg" />),
- %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="true" controls="true" src="/videos/rss.m4v" />),
- %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="true" src="/videos/rss.m4v" />),
+ %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="autoplay" controls="controls" src="/videos/rss.m4v" />),
+ %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="autobuffer" src="/videos/rss.m4v" />),
%(video_tag("gold.m4v", :size => "160x120")) => %(<video height="120" src="/videos/gold.m4v" width="160" />),
%(video_tag("gold.m4v", "size" => "320x240")) => %(<video height="240" src="/videos/gold.m4v" width="320" />),
%(video_tag("trailer.ogg", :poster => "screenshot.png")) => %(<video poster="/images/screenshot.png" src="/videos/trailer.ogg" />),
@@ -168,7 +168,7 @@ class AssetTagHelperTest < ActionView::TestCase
%(video_tag("error.avi", "size" => "x")) => %(<video src="/videos/error.avi" />),
%(video_tag("http://media.rubyonrails.org/video/rails_blog_2.mov")) => %(<video src="http://media.rubyonrails.org/video/rails_blog_2.mov" />),
%(video_tag(["multiple.ogg", "multiple.avi"])) => %(<video><source src="multiple.ogg" /><source src="multiple.avi" /></video>),
- %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="true" height="120" width="160"><source src="multiple.ogg" /><source src="multiple.avi" /></video>)
+ %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="controls" height="120" width="160"><source src="multiple.ogg" /><source src="multiple.avi" /></video>)
}
AudioPathToTag = {
@@ -187,7 +187,7 @@ class AssetTagHelperTest < ActionView::TestCase
AudioLinkToTag = {
%(audio_tag("xml.wav")) => %(<audio src="/audios/xml.wav" />),
- %(audio_tag("rss.wav", :autoplay => true, :controls => true)) => %(<audio autoplay="true" controls="true" src="/audios/rss.wav" />),
+ %(audio_tag("rss.wav", :autoplay => true, :controls => true)) => %(<audio autoplay="autoplay" controls="controls" src="/audios/rss.wav" />),
%(audio_tag("http://media.rubyonrails.org/audio/rails_blog_2.mov")) => %(<audio src="http://media.rubyonrails.org/audio/rails_blog_2.mov" />),
}
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index ffb44e3824..b8c2a367b4 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -2,7 +2,7 @@ require 'active_support/inflector'
module ActiveModel
class Name < String
- attr_reader :singular, :plural, :element, :collection, :partial_path
+ attr_reader :singular, :plural, :element, :collection, :partial_path, :human
alias_method :cache_key, :collection
def initialize(name)
@@ -10,6 +10,7 @@ module ActiveModel
@singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
+ @human = @element.gsub(/_/, " ")
@collection = ActiveSupport::Inflector.tableize(self).freeze
@partial_path = "#{@collection}/#{@element}".freeze
end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 76a0e54a56..4508a39347 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -90,7 +90,7 @@ module ActiveModel
end
def root
- root = (options[:root] || @serializable.class.to_s.underscore).to_s
+ root = (options[:root] || @serializable.class.model_name.singular).to_s
reformat_name(root)
end
@@ -152,7 +152,11 @@ module ActiveModel
def add_procs
if procs = options.delete(:procs)
[ *procs ].each do |proc|
- proc.call(options)
+ if proc.arity > 1
+ proc.call(options, @serializable)
+ else
+ proc.call(options)
+ end
end
end
end
diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb
index e459f6433a..428e5a6bd1 100644
--- a/activemodel/test/cases/serializeration/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb
@@ -9,6 +9,11 @@ class Contact
end
end
+module Admin
+ class Contact < ::Contact
+ end
+end
+
class XmlSerializationTest < ActiveModel::TestCase
def setup
@contact = Contact.new
@@ -25,6 +30,12 @@ class XmlSerializationTest < ActiveModel::TestCase
assert_match %r{</contact>$}, @xml
end
+ test "should serialize namespaced root" do
+ @xml = Admin::Contact.new(@contact.attributes).to_xml
+ assert_match %r{^<admin-contact>}, @xml
+ assert_match %r{</admin-contact>$}, @xml
+ end
+
test "should serialize default root with namespace" do
@xml = @contact.to_xml :namespace => "http://xml.rubyonrails.org/contact"
assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, @xml
@@ -82,4 +93,16 @@ class XmlSerializationTest < ActiveModel::TestCase
test "should serialize yaml" do
assert_match %r{<preferences type=\"yaml\">--- \n:gem: ruby\n</preferences>}, @contact.to_xml
end
+
+ test "should call proc on object" do
+ proc = Proc.new { |options| options[:builder].tag!('nationality', 'unknown') }
+ xml = @contact.to_xml(:procs => [ proc ])
+ assert_match %r{<nationality>unknown</nationality>}, xml
+ end
+
+ test 'should supply serializable to second proc argument' do
+ proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
+ xml = @contact.to_xml(:procs => [ proc ])
+ assert_match %r{<name-reverse>kcats noraa</name-reverse>}, xml
+ end
end
diff --git a/activemodel/test/models/contact.rb b/activemodel/test/models/contact.rb
index 7d69c91996..f9fb0af027 100644
--- a/activemodel/test/models/contact.rb
+++ b/activemodel/test/models/contact.rb
@@ -1,3 +1,7 @@
class Contact
attr_accessor :name, :age, :created_at, :awesome, :preferences
+
+ def initialize(options = {})
+ options.each { |name, value| send("#{name}=", value) }
+ end
end
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 009071e1d4..ab271291c5 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -37,7 +37,7 @@ rescue LoadError
end
module ActiveRecord
- # TODO: Review explicit loads to see if they will automatically be handled by the initilizer.
+ # TODO: Review explicit loads to see if they will automatically be handled by the initializer.
def self.load_all!
[Base, DynamicFinderMatch, ConnectionAdapters::AbstractAdapter]
end
@@ -57,7 +57,6 @@ module ActiveRecord
autoload :Batches, 'active_record/batches'
autoload :Calculations, 'active_record/calculations'
autoload :Callbacks, 'active_record/callbacks'
- autoload :Dirty, 'active_record/dirty'
autoload :DynamicFinderMatch, 'active_record/dynamic_finder_match'
autoload :DynamicScopeMatch, 'active_record/dynamic_scope_match'
autoload :Migration, 'active_record/migration'
@@ -76,6 +75,16 @@ module ActiveRecord
autoload :Transactions, 'active_record/transactions'
autoload :Validations, 'active_record/validations'
+ module AttributeMethods
+ autoload :BeforeTypeCast, 'active_record/attribute_methods/before_type_cast'
+ autoload :Dirty, 'active_record/attribute_methods/dirty'
+ autoload :PrimaryKey, 'active_record/attribute_methods/primary_key'
+ autoload :Query, 'active_record/attribute_methods/query'
+ autoload :Read, 'active_record/attribute_methods/read'
+ autoload :TimeZoneConversion, 'active_record/attribute_methods/time_zone_conversion'
+ autoload :Write, 'active_record/attribute_methods/write'
+ end
+
module Locking
autoload :Optimistic, 'active_record/locking/optimistic'
autoload :Pessimistic, 'active_record/locking/pessimistic'
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index af80a579d6..e41fda7a4b 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -28,7 +28,7 @@ module ActiveRecord
# 'books' table is useful; the joined 'authors' data is just redundant, and
# processing this redundant data takes memory and CPU time. The problem
# quickly becomes worse and worse as the level of eager loading increases
- # (i.e. if ActiveRecord is to eager load the associations' assocations as
+ # (i.e. if ActiveRecord is to eager load the associations' associations as
# well).
#
# The second strategy is to use multiple database queries, one for each
@@ -58,7 +58,7 @@ module ActiveRecord
# +associations+ specifies one or more associations that you want to
# preload. It may be:
# - a Symbol or a String which specifies a single association name. For
- # example, specifiying +:books+ allows this method to preload all books
+ # example, specifying +:books+ allows this method to preload all books
# for an Author.
# - an Array which specifies multiple association names. This array
# is processed recursively. For example, specifying <tt>[:avatar, :books]</tt>
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a0aeff68b6..60da632b3b 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -519,13 +519,13 @@ module ActiveRecord
#
# Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])
#
- # will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
+ # This will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
# <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences.
# In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole
# and not just to the association. You must disambiguate column references for this fallback to happen, for example
# <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
#
- # If you do want eagerload only some members of an association it is usually more natural to <tt>:include</tt> an association
+ # If you do want eager load only some members of an association it is usually more natural to <tt>:include</tt> an association
# which has conditions defined on it:
#
# class Post < ActiveRecord::Base
@@ -534,7 +534,7 @@ module ActiveRecord
#
# Post.find(:all, :include => :approved_comments)
#
- # will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
+ # This will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
#
# If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects:
#
@@ -557,7 +557,7 @@ module ActiveRecord
#
# Address.find(:all, :include => :addressable)
#
- # will execute one query to load the addresses and load the addressables with one query per addressable type.
+ # This will execute one query to load the addresses and load the addressables with one query per addressable type.
# For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of
# addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback
# to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
@@ -641,6 +641,60 @@ module ActiveRecord
# end
# end
#
+ # == Bi-directional associations
+ #
+ # When you specify an association there is usually an association on the associated model that specifies the same
+ # relationship in reverse. For example, with the following models:
+ #
+ # class Dungeon < ActiveRecord::Base
+ # has_many :traps
+ # has_one :evil_wizard
+ # end
+ #
+ # class Trap < ActiveRecord::Base
+ # belongs_to :dungeon
+ # end
+ #
+ # class EvilWizard < ActiveRecord::Base
+ # belongs_to :dungeon
+ # end
+ #
+ # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are the inverse of each other and the
+ # inverse of the +dungeon+ association on +EvilWizard+ is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
+ # +ActiveRecord+ doesn't do know anything about these inverse relationships and so no object loading optimisation is possible. For example:
+ #
+ # d = Dungeon.first
+ # t = d.traps.first
+ # d.level == t.dungeon.level # => true
+ # d.level = 10
+ # d.level == t.dungeon.level # => false
+ #
+ # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to the same object data from the database, but are
+ # actually different in-memory copies of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
+ # +ActiveRecord+ about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to:
+ #
+ # class Dungeon < ActiveRecord::Base
+ # has_many :traps, :inverse_of => :dungeon
+ # has_one :evil_wizard, :inverse_of => :dungeon
+ # end
+ #
+ # class Trap < ActiveRecord::Base
+ # belongs_to :dungeon, :inverse_of => :traps
+ # end
+ #
+ # class EvilWizard < ActiveRecord::Base
+ # belongs_to :dungeon, :inverse_of => :evil_wizard
+ # end
+ #
+ # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same in-memory instance and our final <tt>d.level == t.dungeon.level</tt>
+ # will return +true+.
+ #
+ # There are limitations to <tt>:inverse_of</tt> support:
+ #
+ # * does not work with <tt>:through</tt> associations.
+ # * does not work with <tt>:polymorphic</tt> associations.
+ # * for +belongs_to+ associations +has_many+ inverse associations are ignored.
+ #
# == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
#
# If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll
@@ -781,6 +835,10 @@ module ActiveRecord
# If false, don't validate the associated objects when saving the parent object. true by default.
# [:autosave]
# If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
+ # [:inverse_of]
+ # Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_many</tt>
+ # association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options.
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
#
# Option examples:
# has_many :comments, :order => "posted_on"
@@ -890,6 +948,10 @@ module ActiveRecord
# If false, don't validate the associated object when saving the parent object. +false+ by default.
# [:autosave]
# If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
+ # [:inverse_of]
+ # Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_one</tt>
+ # association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options.
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
#
# Option examples:
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
@@ -992,6 +1054,10 @@ module ActiveRecord
# [:touch]
# If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or
# destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute.
+ # [:inverse_of]
+ # Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated object that is the inverse of this <tt>belongs_to</tt>
+ # association. Does not work in combination with the <tt>:polymorphic</tt> options.
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
#
# Option examples:
# belongs_to :firm, :foreign_key => "client_of"
@@ -1201,7 +1267,7 @@ module ActiveRecord
private
# Generates a join table name from two provided table names.
- # The names in the join table namesme end up in lexicographic order.
+ # The names in the join table names end up in lexicographic order.
#
# join_table_name("members", "clubs") # => "clubs_members"
# join_table_name("members", "special_clubs") # => "members_special_clubs"
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 84edaec15e..e67ccfb228 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -11,7 +11,7 @@ module ActiveRecord
# ones created with +build+ are added to the target. So, the target may be
# non-empty and still lack children waiting to be read from the database.
# If you look directly to the database you cannot assume that's the entire
- # collection because new records may have beed added to the target, etc.
+ # collection because new records may have been added to the target, etc.
#
# If you need to work on all current children, new and existing records,
# +load_target+ and the +loaded+ flag are your friends.
@@ -228,7 +228,7 @@ module ActiveRecord
self
end
- # Destory all the records from this association.
+ # Destroy all the records from this association.
#
# See destroy for more info.
def destroy_all
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index ecd2d57a5a..5cb536af1f 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -4,22 +4,6 @@ module ActiveRecord
module AttributeMethods #:nodoc:
extend ActiveSupport::Concern
- DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
- ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
-
- included do
- attribute_method_suffix(*DEFAULT_SUFFIXES)
-
- cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
- self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
-
- cattr_accessor :time_zone_aware_attributes, :instance_writer => false
- self.time_zone_aware_attributes = false
-
- class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
- self.skip_time_zone_conversion_for_attributes = []
- end
-
# Declare and check for suffixed attribute methods.
module ClassMethods
# Declares a method available for all attributes with the given suffix.
@@ -50,8 +34,39 @@ module ActiveRecord
# person.name = 'Hubert'
# person.name_changed? # => true
def attribute_method_suffix(*suffixes)
- attribute_method_suffixes.concat suffixes
+ attribute_method_suffixes.concat(suffixes)
rebuild_attribute_method_regexp
+ undefine_attribute_methods
+ end
+
+ # Defines an "attribute" method (like +inheritance_column+ or
+ # +table_name+). A new (class) method will be created with the
+ # given name. If a value is specified, the new method will
+ # return that value (as a string). Otherwise, the given block
+ # will be used to compute the value of the method.
+ #
+ # The original method will be aliased, with the new name being
+ # prefixed with "original_". This allows the new method to
+ # access the original value.
+ #
+ # Example:
+ #
+ # class A < ActiveRecord::Base
+ # define_attr_method :primary_key, "sysid"
+ # define_attr_method( :inheritance_column ) do
+ # original_inheritance_column + "_id"
+ # end
+ # end
+ def define_attr_method(name, value=nil, &block)
+ sing = metaclass
+ sing.send :alias_method, "original_#{name}", name
+ if block_given?
+ sing.send :define_method, name, &block
+ else
+ # use eval instead of a block to work around a memory leak in dev
+ # mode in fcgi
+ sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
+ end
end
# Returns MatchData if method_name is an attribute method.
@@ -60,173 +75,77 @@ module ActiveRecord
@@attribute_method_regexp.match(method_name)
end
-
# Contains the names of the generated attribute methods.
def generated_methods #:nodoc:
@generated_methods ||= Set.new
end
-
+
def generated_methods?
!generated_methods.empty?
end
-
+
# Generates all the attribute related methods for columns in the database
# accessors, mutators and query methods.
def define_attribute_methods
return if generated_methods?
- columns_hash.each do |name, column|
- unless instance_method_already_implemented?(name)
- if self.serialized_attributes[name]
- define_read_method_for_serialized_attribute(name)
- elsif create_time_zone_conversion_attribute?(name, column)
- define_read_method_for_time_zone_conversion(name)
- else
- define_read_method(name.to_sym, name, column)
- end
- end
-
- unless instance_method_already_implemented?("#{name}=")
- if create_time_zone_conversion_attribute?(name, column)
- define_write_method_for_time_zone_conversion(name)
- else
- define_write_method(name.to_sym)
+ columns_hash.keys.each do |name|
+ attribute_method_suffixes.each do |suffix|
+ method_name = "#{name}#{suffix}"
+ unless instance_method_already_implemented?(method_name)
+ generate_method = "define_attribute_method#{suffix}"
+ if respond_to?(generate_method)
+ send(generate_method, name)
+ else
+ evaluate_attribute_method("def #{method_name}(*args); send(:attribute#{suffix}, '#{name}', *args); end", method_name)
+ end
end
end
-
- unless instance_method_already_implemented?("#{name}?")
- define_question_method(name)
- end
end
end
+ def undefine_attribute_methods
+ generated_methods.each { |name| undef_method(name) }
+ @generated_methods = nil
+ end
+
# Checks whether the method is defined in the model or any of its subclasses
# that also derive from Active Record. Raises DangerousAttributeError if the
# method is defined by Active Record though.
def instance_method_already_implemented?(method_name)
method_name = method_name.to_s
- return true if method_name =~ /^id(=$|\?$|$)/
@_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
@@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map{|m| m.to_s }.to_set
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
@_defined_class_methods.include?(method_name)
end
-
- alias :define_read_methods :define_attribute_methods
-
- # +cache_attributes+ allows you to declare which converted attribute values should
- # be cached. Usually caching only pays off for attributes with expensive conversion
- # methods, like time related columns (e.g. +created_at+, +updated_at+).
- def cache_attributes(*attribute_names)
- attribute_names.each {|attr| cached_attributes << attr.to_s}
- end
-
- # Returns the attributes which are cached. By default time related columns
- # with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
- def cached_attributes
- @cached_attributes ||=
- columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map{|col| col.name}.to_set
- end
-
- # Returns +true+ if the provided attribute is being cached.
- def cache_attribute?(attr_name)
- cached_attributes.include?(attr_name)
- end
private
-
# Suffixes a, ?, c become regexp /(a|\?|c)$/
def rebuild_attribute_method_regexp
suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
@@attribute_method_regexp = /(#{suffixes.join('|')})$/.freeze
end
- # Default to =, ?, _before_type_cast
def attribute_method_suffixes
@@attribute_method_suffixes ||= []
end
-
- def create_time_zone_conversion_attribute?(name, column)
- time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
- end
-
- # Define an attribute reader method. Cope with nil column.
- def define_read_method(symbol, attr_name, column)
- cast_code = column.type_cast_code('v') if column
- access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
-
- unless attr_name.to_s == self.primary_key.to_s
- access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
- end
-
- if cache_attribute?(attr_name)
- access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
- end
- evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
- end
-
- # Define read method for serialized attribute.
- def define_read_method_for_serialized_attribute(attr_name)
- evaluate_attribute_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
- end
-
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
- # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
- def define_read_method_for_time_zone_conversion(attr_name)
- method_body = <<-EOV
- def #{attr_name}(reload = false)
- cached = @attributes_cache['#{attr_name}']
- return cached if cached && !reload
- time = read_attribute('#{attr_name}')
- @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
- end
- EOV
- evaluate_attribute_method attr_name, method_body
- end
-
- # Defines a predicate method <tt>attr_name?</tt>.
- def define_question_method(attr_name)
- evaluate_attribute_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end", "#{attr_name}?"
- end
-
- def define_write_method(attr_name)
- evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
- end
-
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
- # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
- def define_write_method_for_time_zone_conversion(attr_name)
- method_body = <<-EOV
- def #{attr_name}=(time)
- unless time.acts_like?(:time)
- time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
- end
- time = time.in_time_zone rescue nil if time
- write_attribute(:#{attr_name}, time)
- end
- EOV
- evaluate_attribute_method attr_name, method_body, "#{attr_name}="
- end
# Evaluate the definition for an attribute related method
- def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
-
- unless method_name.to_s == primary_key.to_s
- generated_methods << method_name
- end
+ def evaluate_attribute_method(method_definition, method_name)
+ generated_methods << method_name.to_s
begin
class_eval(method_definition, __FILE__, __LINE__)
rescue SyntaxError => err
- generated_methods.delete(attr_name)
+ generated_methods.delete(method_name.to_s)
if logger
logger.warn "Exception occurred during reader method compilation."
- logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
+ logger.warn "Maybe #{method_name} is not a valid Ruby identifier?"
logger.warn err.message
end
end
end
- end # ClassMethods
-
+ end
# Allows access to the object attributes, which are held in the <tt>@attributes</tt> hash, as though they
# were first-class methods. So a Person class with a name attribute can use Person#name and
@@ -248,98 +167,17 @@ module ActiveRecord
return self.send(method_id, *args, &block)
end
end
-
- guard_private_attribute_method!(method_name, args)
- if self.class.primary_key.to_s == method_name
- id
- elsif md = self.class.match_attribute_method?(method_name)
- attribute_name, method_type = md.pre_match, md.to_s
- if @attributes.include?(attribute_name)
- __send__("attribute#{method_type}", attribute_name, *args, &block)
- else
- super
- end
- elsif @attributes.include?(method_name)
- read_attribute(method_name)
- else
- super
- end
- end
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
- def read_attribute(attr_name)
- attr_name = attr_name.to_s
- if !(value = @attributes[attr_name]).nil?
- if column = column_for_attribute(attr_name)
- if unserializable_attribute?(attr_name, column)
- unserialize_attribute(attr_name)
- else
- column.type_cast(value)
- end
- else
- value
+ if md = self.class.match_attribute_method?(method_name)
+ attribute_name, method_type = md.pre_match, md.to_s
+ if attribute_name == 'id' || @attributes.include?(attribute_name)
+ guard_private_attribute_method!(method_name, args)
+ return __send__("attribute#{method_type}", attribute_name, *args, &block)
end
- else
- nil
- end
- end
-
- def read_attribute_before_type_cast(attr_name)
- @attributes[attr_name]
- end
-
- # Returns true if the attribute is of a text column and marked for serialization.
- def unserializable_attribute?(attr_name, column)
- column.text? && self.class.serialized_attributes[attr_name]
- end
-
- # Returns the unserialized object of the attribute.
- def unserialize_attribute(attr_name)
- unserialized_object = object_from_yaml(@attributes[attr_name])
-
- if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
- @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
- else
- raise SerializationTypeMismatch,
- "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
- end
- end
-
-
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
- # columns are turned into +nil+.
- def write_attribute(attr_name, value)
- attr_name = attr_name.to_s
- @attributes_cache.delete(attr_name)
- if (column = column_for_attribute(attr_name)) && column.number?
- @attributes[attr_name] = convert_number_column_value(value)
- else
- @attributes[attr_name] = value
end
+ super
end
-
- def query_attribute(attr_name)
- unless value = read_attribute(attr_name)
- false
- else
- column = self.class.columns_hash[attr_name]
- if column.nil?
- if Numeric === value || value !~ /[^0-9]/
- !value.to_i.zero?
- else
- return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
- !value.blank?
- end
- elsif column.number?
- !value.zero?
- else
- !value.blank?
- end
- end
- end
-
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
# which will all return +true+.
@@ -358,13 +196,9 @@ module ActiveRecord
return true
end
end
-
- if @attributes.nil?
- return super
- elsif @attributes.include?(method_name)
- return true
- elsif md = self.class.match_attribute_method?(method_name)
- return true if @attributes.include?(md.pre_match)
+
+ if md = self.class.match_attribute_method?(method_name)
+ return true if md.pre_match == 'id' || @attributes.include?(md.pre_match)
end
super
end
@@ -376,24 +210,9 @@ module ActiveRecord
raise NoMethodError.new("Attempt to call private method", method_name, args)
end
end
-
+
def missing_attribute(attr_name, stack)
raise ActiveRecord::MissingAttributeError, "missing attribute: #{attr_name}", stack
end
-
- # Handle *? for method_missing.
- def attribute?(attribute_name)
- query_attribute(attribute_name)
- end
-
- # Handle *= for method_missing.
- def attribute=(attribute_name, value)
- write_attribute(attribute_name, value)
- end
-
- # Handle *_before_type_cast for method_missing.
- def attribute_before_type_cast(attribute_name)
- read_attribute_before_type_cast(attribute_name)
- end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
new file mode 100644
index 0000000000..a4e144f233
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -0,0 +1,33 @@
+module ActiveRecord
+ module AttributeMethods
+ module BeforeTypeCast
+ extend ActiveSupport::Concern
+
+ included do
+ attribute_method_suffix "_before_type_cast"
+ end
+
+ def read_attribute_before_type_cast(attr_name)
+ @attributes[attr_name]
+ end
+
+ # Returns a hash of attributes before typecasting and deserialization.
+ def attributes_before_type_cast
+ self.attribute_names.inject({}) do |attrs, name|
+ attrs[name] = read_attribute_before_type_cast(name)
+ attrs
+ end
+ end
+
+ private
+ # Handle *_before_type_cast for method_missing.
+ def attribute_before_type_cast(attribute_name)
+ if attribute_name == 'id'
+ read_attribute_before_type_cast(self.class.primary_key)
+ else
+ read_attribute_before_type_cast(attribute_name)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
new file mode 100644
index 0000000000..b88c84938d
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -0,0 +1,187 @@
+module ActiveRecord
+ module AttributeMethods
+ # Track unsaved attribute changes.
+ #
+ # A newly instantiated object is unchanged:
+ # person = Person.find_by_name('uncle bob')
+ # person.changed? # => false
+ #
+ # Change the name:
+ # person.name = 'Bob'
+ # person.changed? # => true
+ # person.name_changed? # => true
+ # person.name_was # => 'uncle bob'
+ # person.name_change # => ['uncle bob', 'Bob']
+ # person.name = 'Bill'
+ # person.name_change # => ['uncle bob', 'Bill']
+ #
+ # Save the changes:
+ # person.save
+ # person.changed? # => false
+ # person.name_changed? # => false
+ #
+ # Assigning the same value leaves the attribute unchanged:
+ # person.name = 'Bill'
+ # person.name_changed? # => false
+ # person.name_change # => nil
+ #
+ # Which attributes have changed?
+ # person.name = 'bob'
+ # person.changed # => ['name']
+ # person.changes # => { 'name' => ['Bill', 'bob'] }
+ #
+ # Before modifying an attribute in-place:
+ # person.name_will_change!
+ # person.name << 'by'
+ # person.name_change # => ['uncle bob', 'uncle bobby']
+ module Dirty
+ extend ActiveSupport::Concern
+
+ DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was']
+
+ included do
+ attribute_method_suffix *DIRTY_SUFFIXES
+
+ alias_method_chain :save, :dirty
+ alias_method_chain :save!, :dirty
+ alias_method_chain :update, :dirty
+ alias_method_chain :reload, :dirty
+
+ superclass_delegating_accessor :partial_updates
+ self.partial_updates = true
+ end
+
+ # Do any attributes have unsaved changes?
+ # person.changed? # => false
+ # person.name = 'bob'
+ # person.changed? # => true
+ def changed?
+ !changed_attributes.empty?
+ end
+
+ # List of attributes with unsaved changes.
+ # person.changed # => []
+ # person.name = 'bob'
+ # person.changed # => ['name']
+ def changed
+ changed_attributes.keys
+ end
+
+ # Map of changed attrs => [original value, new value].
+ # person.changes # => {}
+ # person.name = 'bob'
+ # person.changes # => { 'name' => ['bill', 'bob'] }
+ def changes
+ changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h }
+ end
+
+ # Attempts to +save+ the record and clears changed attributes if successful.
+ def save_with_dirty(*args) #:nodoc:
+ if status = save_without_dirty(*args)
+ changed_attributes.clear
+ end
+ status
+ end
+
+ # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
+ def save_with_dirty!(*args) #:nodoc:
+ status = save_without_dirty!(*args)
+ changed_attributes.clear
+ status
+ end
+
+ # <tt>reload</tt> the record and clears changed attributes.
+ def reload_with_dirty(*args) #:nodoc:
+ record = reload_without_dirty(*args)
+ changed_attributes.clear
+ record
+ end
+
+ private
+ # Map of change <tt>attr => original value</tt>.
+ def changed_attributes
+ @changed_attributes ||= {}
+ end
+
+ # Handle <tt>*_changed?</tt> for +method_missing+.
+ def attribute_changed?(attr)
+ changed_attributes.include?(attr)
+ end
+
+ # Handle <tt>*_change</tt> for +method_missing+.
+ def attribute_change(attr)
+ [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
+ end
+
+ # Handle <tt>*_was</tt> for +method_missing+.
+ def attribute_was(attr)
+ attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
+ end
+
+ # Handle <tt>*_will_change!</tt> for +method_missing+.
+ def attribute_will_change!(attr)
+ changed_attributes[attr] = clone_attribute_value(:read_attribute, attr)
+ end
+
+ # Wrap write_attribute to remember original attribute value.
+ def write_attribute(attr, value)
+ attr = attr.to_s
+
+ # The attribute already has an unsaved change.
+ if changed_attributes.include?(attr)
+ old = changed_attributes[attr]
+ changed_attributes.delete(attr) unless field_changed?(attr, old, value)
+ else
+ old = clone_attribute_value(:read_attribute, attr)
+ changed_attributes[attr] = old if field_changed?(attr, old, value)
+ end
+
+ # Carry on.
+ super(attr, value)
+ end
+
+ def update_with_dirty
+ if partial_updates?
+ # Serialized attributes should always be written in case they've been
+ # changed in place.
+ update_without_dirty(changed | self.class.serialized_attributes.keys)
+ else
+ update_without_dirty
+ end
+ end
+
+ def field_changed?(attr, old, value)
+ if column = column_for_attribute(attr)
+ if column.number? && column.null && (old.nil? || old == 0) && value.blank?
+ # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
+ # Hence we don't record it as a change if the value changes from nil to ''.
+ # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
+ # be typecast back to 0 (''.to_i => 0)
+ value = nil
+ else
+ value = column.type_cast(value)
+ end
+ end
+
+ old != value
+ end
+
+ module ClassMethods
+ def self.extended(base)
+ class << base
+ alias_method_chain :alias_attribute, :dirty
+ end
+ end
+
+ def alias_attribute_with_dirty(new_name, old_name)
+ alias_attribute_without_dirty(new_name, old_name)
+ DIRTY_SUFFIXES.each do |suffix|
+ module_eval <<-STR, __FILE__, __LINE__+1
+ def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end # def subject_changed?; self.title_changed?; end
+ STR
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
new file mode 100644
index 0000000000..365fdeb55a
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -0,0 +1,44 @@
+module ActiveRecord
+ module AttributeMethods
+ module PrimaryKey
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
+ # primary_key_prefix_type setting, though.
+ def primary_key
+ reset_primary_key
+ end
+
+ def reset_primary_key #:nodoc:
+ key = get_primary_key(base_class.name)
+ set_primary_key(key)
+ key
+ end
+
+ def get_primary_key(base_name) #:nodoc:
+ key = 'id'
+ case primary_key_prefix_type
+ when :table_name
+ key = base_name.to_s.foreign_key(false)
+ when :table_name_with_underscore
+ key = base_name.to_s.foreign_key
+ end
+ key
+ end
+
+ # Sets the name of the primary key column to use to the given value,
+ # or (if the value is nil or false) to the value returned by the given
+ # block.
+ #
+ # class Project < ActiveRecord::Base
+ # set_primary_key "sysid"
+ # end
+ def set_primary_key(value = nil, &block)
+ define_attr_method :primary_key, value, &block
+ end
+ alias :primary_key= :set_primary_key
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
new file mode 100644
index 0000000000..a949d80120
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -0,0 +1,37 @@
+module ActiveRecord
+ module AttributeMethods
+ module Query
+ extend ActiveSupport::Concern
+
+ included do
+ attribute_method_suffix "?"
+ end
+
+ def query_attribute(attr_name)
+ unless value = read_attribute(attr_name)
+ false
+ else
+ column = self.class.columns_hash[attr_name]
+ if column.nil?
+ if Numeric === value || value !~ /[^0-9]/
+ !value.to_i.zero?
+ else
+ return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
+ !value.blank?
+ end
+ elsif column.number?
+ !value.zero?
+ else
+ !value.blank?
+ end
+ end
+ end
+
+ private
+ # Handle *? for method_missing.
+ def attribute?(attribute_name)
+ query_attribute(attribute_name)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
new file mode 100644
index 0000000000..bea332ef26
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -0,0 +1,116 @@
+module ActiveRecord
+ module AttributeMethods
+ module Read
+ extend ActiveSupport::Concern
+
+ ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
+
+ included do
+ attribute_method_suffix ""
+
+ cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
+ self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+
+ # Undefine id so it can be used as an attribute name
+ undef_method :id
+ end
+
+ module ClassMethods
+ # +cache_attributes+ allows you to declare which converted attribute values should
+ # be cached. Usually caching only pays off for attributes with expensive conversion
+ # methods, like time related columns (e.g. +created_at+, +updated_at+).
+ def cache_attributes(*attribute_names)
+ attribute_names.each {|attr| cached_attributes << attr.to_s}
+ end
+
+ # Returns the attributes which are cached. By default time related columns
+ # with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
+ def cached_attributes
+ @cached_attributes ||=
+ columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map{|col| col.name}.to_set
+ end
+
+ # Returns +true+ if the provided attribute is being cached.
+ def cache_attribute?(attr_name)
+ cached_attributes.include?(attr_name)
+ end
+
+ protected
+ def define_attribute_method(attr_name)
+ if self.serialized_attributes[attr_name]
+ define_read_method_for_serialized_attribute(attr_name)
+ else
+ define_read_method(attr_name.to_sym, attr_name, columns_hash[attr_name])
+ end
+
+ if attr_name == primary_key && attr_name != "id"
+ define_read_method(:id, attr_name, columns_hash[attr_name])
+ end
+ end
+
+ private
+ # Define read method for serialized attribute.
+ def define_read_method_for_serialized_attribute(attr_name)
+ evaluate_attribute_method "def #{attr_name}; unserialize_attribute('#{attr_name}'); end", attr_name
+ end
+
+ # Define an attribute reader method. Cope with nil column.
+ def define_read_method(symbol, attr_name, column)
+ cast_code = column.type_cast_code('v') if column
+ access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
+
+ unless attr_name.to_s == self.primary_key.to_s
+ access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
+ end
+
+ if cache_attribute?(attr_name)
+ access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
+ end
+ evaluate_attribute_method "def #{symbol}; #{access_code}; end", symbol
+ end
+ end
+
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+ def read_attribute(attr_name)
+ attr_name = attr_name.to_s
+ attr_name = self.class.primary_key if attr_name == 'id'
+ if !(value = @attributes[attr_name]).nil?
+ if column = column_for_attribute(attr_name)
+ if unserializable_attribute?(attr_name, column)
+ unserialize_attribute(attr_name)
+ else
+ column.type_cast(value)
+ end
+ else
+ value
+ end
+ else
+ nil
+ end
+ end
+
+ # Returns true if the attribute is of a text column and marked for serialization.
+ def unserializable_attribute?(attr_name, column)
+ column.text? && self.class.serialized_attributes[attr_name]
+ end
+
+ # Returns the unserialized object of the attribute.
+ def unserialize_attribute(attr_name)
+ unserialized_object = object_from_yaml(@attributes[attr_name])
+
+ if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
+ @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
+ else
+ raise SerializationTypeMismatch,
+ "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
+ end
+ end
+
+ private
+ def attribute(attribute_name)
+ read_attribute(attribute_name)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
new file mode 100644
index 0000000000..9e2c6174c6
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -0,0 +1,60 @@
+module ActiveRecord
+ module AttributeMethods
+ module TimeZoneConversion
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :time_zone_aware_attributes, :instance_writer => false
+ self.time_zone_aware_attributes = false
+
+ class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
+ self.skip_time_zone_conversion_for_attributes = []
+ end
+
+ module ClassMethods
+ protected
+ # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
+ # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
+ def define_attribute_method(attr_name)
+ if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
+ method_body = <<-EOV
+ def #{attr_name}(reload = false)
+ cached = @attributes_cache['#{attr_name}']
+ return cached if cached && !reload
+ time = read_attribute('#{attr_name}')
+ @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
+ end
+ EOV
+ evaluate_attribute_method method_body, attr_name
+ else
+ super
+ end
+ end
+
+ # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
+ # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
+ def define_attribute_method=(attr_name)
+ if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
+ method_body = <<-EOV
+ def #{attr_name}=(time)
+ unless time.acts_like?(:time)
+ time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
+ end
+ time = time.in_time_zone rescue nil if time
+ write_attribute(:#{attr_name}, time)
+ end
+ EOV
+ evaluate_attribute_method method_body, "#{attr_name}="
+ else
+ super
+ end
+ end
+
+ private
+ def create_time_zone_conversion_attribute?(name, column)
+ time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
new file mode 100644
index 0000000000..497e72ee4a
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -0,0 +1,37 @@
+module ActiveRecord
+ module AttributeMethods
+ module Write
+ extend ActiveSupport::Concern
+
+ included do
+ attribute_method_suffix "="
+ end
+
+ module ClassMethods
+ protected
+ def define_attribute_method=(attr_name)
+ evaluate_attribute_method "def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", "#{attr_name}="
+ end
+ end
+
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
+ # columns are turned into +nil+.
+ def write_attribute(attr_name, value)
+ attr_name = attr_name.to_s
+ attr_name = self.class.primary_key if attr_name == 'id'
+ @attributes_cache.delete(attr_name)
+ if (column = column_for_attribute(attr_name)) && column.number?
+ @attributes[attr_name] = convert_number_column_value(value)
+ else
+ @attributes[attr_name] = value
+ end
+ end
+
+ private
+ # Handle *= for method_missing.
+ def attribute=(attribute_name, value)
+ write_attribute(attribute_name, value)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 9a3a02870a..2b50333682 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -256,6 +256,12 @@ module ActiveRecord #:nodoc:
#
# Student.find(:all, :conditions => { :grade => [9,11,12] })
#
+ # When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a
+ # particular condition. For instance:
+ #
+ # Student.find(:all, :conditions => { :schools => { :type => 'public' }}, :joins => :schools)
+ # Student.find(:all, :conditions => { 'schools.type' => 'public' }, :joins => :schools)
+ #
# == Overwriting default accessors
#
# All column values are automatically available through basic accessors on the Active Record object, but sometimes you
@@ -858,7 +864,7 @@ module ActiveRecord #:nodoc:
# Book.update_all "author = 'David'", "title LIKE '%Rails%'"
#
# # Update all avatars migrated more than a week ago
- # Avatar.update_all ['migrated_at = ?, Time.now.utc], ['migrated_at > ?', 1.week.ago]
+ # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
#
# # 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
@@ -1058,6 +1064,21 @@ module ActiveRecord #:nodoc:
#
# To start from an all-closed default and enable attributes as needed,
# have a look at +attr_accessible+.
+ #
+ # If the access logic of your application is richer you can use <tt>Hash#except</tt>
+ # or <tt>Hash#slice</tt> to sanitize the hash of parameters before they are
+ # passed to Active Record.
+ #
+ # For example, it could be the case that the list of protected attributes
+ # for a given model depends on the role of the user:
+ #
+ # # Assumes plan_id is not protected because it depends on the role.
+ # params[:account] = params[:account].except(:plan_id) unless admin?
+ # @account.update_attributes(params[:account])
+ #
+ # Note that +attr_protected+ is still applied to the received hash. Thus,
+ # with this technique you can at most _extend_ the list of protected
+ # attributes for a particular mass-assignment call.
def attr_protected(*attributes)
write_inheritable_attribute(:attr_protected, Set.new(attributes.map {|a| a.to_s}) + (protected_attributes || []))
end
@@ -1091,6 +1112,21 @@ module ActiveRecord #:nodoc:
#
# customer.credit_rating = "Average"
# customer.credit_rating # => "Average"
+ #
+ # If the access logic of your application is richer you can use <tt>Hash#except</tt>
+ # or <tt>Hash#slice</tt> to sanitize the hash of parameters before they are
+ # passed to Active Record.
+ #
+ # For example, it could be the case that the list of accessible attributes
+ # for a given model depends on the role of the user:
+ #
+ # # Assumes plan_id is accessible because it depends on the role.
+ # params[:account] = params[:account].except(:plan_id) unless admin?
+ # @account.update_attributes(params[:account])
+ #
+ # Note that +attr_accessible+ is still applied to the received hash. Thus,
+ # with this technique you can at most _narrow_ the list of accessible
+ # attributes for a particular mass-assignment call.
def attr_accessible(*attributes)
write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || []))
end
@@ -1192,29 +1228,6 @@ module ActiveRecord #:nodoc:
name
end
- # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
- # primary_key_prefix_type setting, though.
- def primary_key
- reset_primary_key
- end
-
- def reset_primary_key #:nodoc:
- key = get_primary_key(base_class.name)
- set_primary_key(key)
- key
- end
-
- def get_primary_key(base_name) #:nodoc:
- key = 'id'
- case primary_key_prefix_type
- when :table_name
- key = base_name.to_s.foreign_key(false)
- when :table_name_with_underscore
- key = base_name.to_s.foreign_key
- end
- key
- end
-
# Defines the column name for use with single table inheritance
# -- can be set in subclasses like so: self.inheritance_column = "type_id"
def inheritance_column
@@ -1244,18 +1257,6 @@ module ActiveRecord #:nodoc:
end
alias :table_name= :set_table_name
- # Sets the name of the primary key column to use to the given value,
- # or (if the value is nil or false) to the value returned by the given
- # block.
- #
- # class Project < ActiveRecord::Base
- # set_primary_key "sysid"
- # end
- def set_primary_key(value = nil, &block)
- define_attr_method :primary_key, value, &block
- end
- alias :primary_key= :set_primary_key
-
# Sets the name of the inheritance column to use to the given value,
# or (if the value # is nil or false) to the value returned by the
# given block.
@@ -1368,8 +1369,8 @@ module ActiveRecord #:nodoc:
# end
# end
def reset_column_information
- generated_methods.each { |name| undef_method(name) }
- @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @generated_methods = @inheritance_column = nil
+ undefine_attribute_methods
+ @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
end
def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
@@ -1385,14 +1386,14 @@ module ActiveRecord #:nodoc:
classes
rescue
# OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column'
- # Appearantly the method base_class causes some trouble.
+ # Apparently the method base_class causes some trouble.
# It now works for sure.
[self]
end
# Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
# Person.human_attribute_name("first_name") # => "First name"
- # This used to be depricated in favor of humanize, but is now preferred, because it automatically uses the I18n
+ # This used to be deprecated in favor of humanize, but is now preferred, because it automatically uses the I18n
# module now.
# Specify +options+ with additional translating options.
def human_attribute_name(attribute_key_name, options = {})
@@ -2043,36 +2044,6 @@ module ActiveRecord #:nodoc:
end
end
- # Defines an "attribute" method (like +inheritance_column+ or
- # +table_name+). A new (class) method will be created with the
- # given name. If a value is specified, the new method will
- # return that value (as a string). Otherwise, the given block
- # will be used to compute the value of the method.
- #
- # The original method will be aliased, with the new name being
- # prefixed with "original_". This allows the new method to
- # access the original value.
- #
- # Example:
- #
- # class A < ActiveRecord::Base
- # define_attr_method :primary_key, "sysid"
- # define_attr_method( :inheritance_column ) do
- # original_inheritance_column + "_id"
- # end
- # end
- def define_attr_method(name, value=nil, &block)
- sing = metaclass
- sing.send :alias_method, "original_#{name}", name
- if block_given?
- sing.send :define_method, name, &block
- else
- # use eval instead of a block to work around a memory leak in dev
- # mode in fcgi
- sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
- end
- end
-
protected
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
# method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
@@ -2474,18 +2445,6 @@ module ActiveRecord #:nodoc:
result
end
- # A model instance's primary key is always available as model.id
- # whether you name it the default 'id' or set it to something else.
- def id
- attr_name = self.class.primary_key
- column = column_for_attribute(attr_name)
-
- self.class.send(:define_read_method, :id, attr_name, column)
- # now that the method exists, call it
- self.send attr_name.to_sym
-
- end
-
# Returns a String, which Action Pack uses for constructing an URL to this
# object. The default implementation returns this record's id as a String,
# or nil if this record's unsaved.
@@ -2531,19 +2490,10 @@ module ActiveRecord #:nodoc:
end
end
- def id_before_type_cast #:nodoc:
- read_attribute_before_type_cast(self.class.primary_key)
- end
-
def quoted_id #:nodoc:
quote_value(id, column_for_attribute(self.class.primary_key))
end
- # Sets the primary ID.
- def id=(value)
- write_attribute(self.class.primary_key, value)
- end
-
# Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false.
def new_record?
@new_record || false
@@ -2784,14 +2734,6 @@ module ActiveRecord #:nodoc:
end
end
- # Returns a hash of attributes before typecasting and deserialization.
- def attributes_before_type_cast
- self.attribute_names.inject({}) do |attrs, name|
- attrs[name] = read_attribute_before_type_cast(name)
- attrs
- end
- end
-
# Returns an <tt>#inspect</tt>-like string for the value of the
# attribute +attr_name+. String attributes are elided after 50
# characters, and Date and Time attributes are returned in the
@@ -3191,7 +3133,10 @@ module ActiveRecord #:nodoc:
include Validations
include Locking::Optimistic, Locking::Pessimistic
include AttributeMethods
- include Dirty
+ include AttributeMethods::Read, AttributeMethods::Write, AttributeMethods::BeforeTypeCast, AttributeMethods::Query
+ include AttributeMethods::PrimaryKey
+ include AttributeMethods::TimeZoneConversion
+ include AttributeMethods::Dirty
include Callbacks, ActiveModel::Observing, Timestamp
include Associations, AssociationPreload, NamedScope
include ActiveModel::Conversion
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 3aa0b8f1b5..4a2ec5bf95 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -22,7 +22,7 @@ module ActiveRecord
# * (8) <tt>after_save</tt>
#
# That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
- # Active Record lifecycle. The sequence for calling <tt>Base#save</tt> an existing record is similar, except that each
+ # Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each
# <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback.
#
# Examples:
@@ -262,6 +262,10 @@ module ActiveRecord
# Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
# Note that this callback is still wrapped in the transaction around +save+. For example, if you
# invoke an external indexer at this point it won't see the changes in the database.
+ #
+ # class Contact < ActiveRecord::Base
+ # after_create { |record| logger.info( "Contact #{record.id} was created." ) }
+ # end
def after_create() end
def create_with_callbacks #:nodoc:
return false if callback(:before_create) == false
@@ -272,11 +276,19 @@ module ActiveRecord
private :create_with_callbacks
# Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
+ #
+ # class Contact < ActiveRecord::Base
+ # before_update { |record| logger.info( "Contact #{record.id} is about to be updated." ) }
+ # end
def before_update() end
# Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
# Note that this callback is still wrapped in the transaction around +save+. For example, if you
# invoke an external indexer at this point it won't see the changes in the database.
+ #
+ # class Contact < ActiveRecord::Base
+ # after_update { |record| logger.info( "Contact #{record.id} was updated." ) }
+ # end
def after_update() end
def update_with_callbacks(*args) #:nodoc:
@@ -326,6 +338,10 @@ module ActiveRecord
#
# Note: If you need to _destroy_ or _nullify_ associated records first,
# use the <tt>:dependent</tt> option on your associations.
+ #
+ # class Contact < ActiveRecord::Base
+ # after_destroy { |record| logger.info( "Contact #{record.id} is about to be destroyed." ) }
+ # end
def before_destroy() end
# Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 500dafdc2e..12253eac3f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -362,7 +362,7 @@ module ActiveRecord
def call(env)
@app.call(env)
ensure
- # Don't return connection (and peform implicit rollback) if
+ # Don't return connection (and perform implicit rollback) if
# this request is a part of integration test
unless env.key?("rack.test")
ActiveRecord::Base.clear_active_connections!
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 8e33681772..b2c5c78bf7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -41,11 +41,19 @@ module ActiveRecord
# # create_table() passes a TableDefinition object to the block.
# # This form will not only create the table, but also columns for the
# # table.
+ #
# create_table(:suppliers) do |t|
# t.column :name, :string, :limit => 60
# # Other fields here
# end
#
+ # === Block form, with shorthand
+ # # You can also use the column types as method calls, rather than calling the column method.
+ # create_table(:suppliers) do |t|
+ # t.string :name, :limit => 60
+ # # Other fields here
+ # end
+ #
# === Regular form
# # Creates a table called 'suppliers' with no columns.
# create_table(:suppliers)
diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb
deleted file mode 100644
index 178767e0c3..0000000000
--- a/activerecord/lib/active_record/dirty.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-module ActiveRecord
- # Track unsaved attribute changes.
- #
- # A newly instantiated object is unchanged:
- # person = Person.find_by_name('uncle bob')
- # person.changed? # => false
- #
- # Change the name:
- # person.name = 'Bob'
- # person.changed? # => true
- # person.name_changed? # => true
- # person.name_was # => 'uncle bob'
- # person.name_change # => ['uncle bob', 'Bob']
- # person.name = 'Bill'
- # person.name_change # => ['uncle bob', 'Bill']
- #
- # Save the changes:
- # person.save
- # person.changed? # => false
- # person.name_changed? # => false
- #
- # Assigning the same value leaves the attribute unchanged:
- # person.name = 'Bill'
- # person.name_changed? # => false
- # person.name_change # => nil
- #
- # Which attributes have changed?
- # person.name = 'bob'
- # person.changed # => ['name']
- # person.changes # => { 'name' => ['Bill', 'bob'] }
- #
- # Before modifying an attribute in-place:
- # person.name_will_change!
- # person.name << 'by'
- # person.name_change # => ['uncle bob', 'uncle bobby']
- module Dirty
- extend ActiveSupport::Concern
-
- DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was']
-
- included do
- attribute_method_suffix *DIRTY_SUFFIXES
-
- alias_method_chain :write_attribute, :dirty
- alias_method_chain :save, :dirty
- alias_method_chain :save!, :dirty
- alias_method_chain :update, :dirty
- alias_method_chain :reload, :dirty
-
- superclass_delegating_accessor :partial_updates
- self.partial_updates = true
- end
-
- # Do any attributes have unsaved changes?
- # person.changed? # => false
- # person.name = 'bob'
- # person.changed? # => true
- def changed?
- !changed_attributes.empty?
- end
-
- # List of attributes with unsaved changes.
- # person.changed # => []
- # person.name = 'bob'
- # person.changed # => ['name']
- def changed
- changed_attributes.keys
- end
-
- # Map of changed attrs => [original value, new value].
- # person.changes # => {}
- # person.name = 'bob'
- # person.changes # => { 'name' => ['bill', 'bob'] }
- def changes
- changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h }
- end
-
- # Attempts to +save+ the record and clears changed attributes if successful.
- def save_with_dirty(*args) #:nodoc:
- if status = save_without_dirty(*args)
- changed_attributes.clear
- end
- status
- end
-
- # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
- def save_with_dirty!(*args) #:nodoc:
- status = save_without_dirty!(*args)
- changed_attributes.clear
- status
- end
-
- # <tt>reload</tt> the record and clears changed attributes.
- def reload_with_dirty(*args) #:nodoc:
- record = reload_without_dirty(*args)
- changed_attributes.clear
- record
- end
-
- private
- # Map of change <tt>attr => original value</tt>.
- def changed_attributes
- @changed_attributes ||= {}
- end
-
- # Handle <tt>*_changed?</tt> for +method_missing+.
- def attribute_changed?(attr)
- changed_attributes.include?(attr)
- end
-
- # Handle <tt>*_change</tt> for +method_missing+.
- def attribute_change(attr)
- [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
- end
-
- # Handle <tt>*_was</tt> for +method_missing+.
- def attribute_was(attr)
- attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
- end
-
- # Handle <tt>*_will_change!</tt> for +method_missing+.
- def attribute_will_change!(attr)
- changed_attributes[attr] = clone_attribute_value(:read_attribute, attr)
- end
-
- # Wrap write_attribute to remember original attribute value.
- def write_attribute_with_dirty(attr, value)
- attr = attr.to_s
-
- # The attribute already has an unsaved change.
- if changed_attributes.include?(attr)
- old = changed_attributes[attr]
- changed_attributes.delete(attr) unless field_changed?(attr, old, value)
- else
- old = clone_attribute_value(:read_attribute, attr)
- changed_attributes[attr] = old if field_changed?(attr, old, value)
- end
-
- # Carry on.
- write_attribute_without_dirty(attr, value)
- end
-
- def update_with_dirty
- if partial_updates?
- # Serialized attributes should always be written in case they've been
- # changed in place.
- update_without_dirty(changed | self.class.serialized_attributes.keys)
- else
- update_without_dirty
- end
- end
-
- def field_changed?(attr, old, value)
- if column = column_for_attribute(attr)
- if column.number? && column.null && (old.nil? || old == 0) && value.blank?
- # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
- # Hence we don't record it as a change if the value changes from nil to ''.
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
- # be typecast back to 0 (''.to_i => 0)
- value = nil
- else
- value = column.type_cast(value)
- end
- end
-
- old != value
- end
-
- module ClassMethods
- def self.extended(base)
- class << base
- alias_method_chain :alias_attribute, :dirty
- end
- end
-
- def alias_attribute_with_dirty(new_name, old_name)
- alias_attribute_without_dirty(new_name, old_name)
- DIRTY_SUFFIXES.each do |suffix|
- module_eval <<-STR, __FILE__, __LINE__+1
- def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end # def subject_changed?; self.title_changed?; end
- STR
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 2b0cfc2c3b..6eeeddc9e1 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -409,7 +409,7 @@ end
# subdomain: $LABEL
#
# Also, sometimes (like when porting older join table fixtures) you'll need
-# to be able to get ahold of the identifier for a given label. ERB
+# to be able to get a hold of the identifier for a given label. ERB
# to the rescue:
#
# george_reginald:
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index f7cc244471..7631f3ec35 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -109,8 +109,8 @@ module ActiveRecord
# script/generate migration MyNewMigration
#
# where MyNewMigration is the name of your migration. The generator will
- # create an empty migration file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
- # directory where <tt>nnn</tt> is the next largest migration number.
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
+ # directory where <tt>timestamp</tt> is the UTC formatted date and time that the migration was generated.
#
# You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
# MyNewMigration.
@@ -118,7 +118,7 @@ module ActiveRecord
# There is a special syntactic shortcut to generate migrations that add fields to a table.
# script/generate migration add_fieldname_to_tablename fieldname:string
#
- # This will generate the file <tt>nnn_add_fieldname_to_tablename</tt>, which will look like this:
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
# class AddFieldnameToTablename < ActiveRecord::Migration
# def self.up
# add_column :tablenames, :fieldname, :string
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index dd2a90b8e5..bbe2d1f205 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -92,12 +92,7 @@ module ActiveRecord
when Hash
options
when Proc
- case parent_scope
- when Scope
- with_scope(:find => parent_scope.proxy_options) { options.call(*args) }
- else
- options.call(*args)
- end
+ options.call(*args)
end, &block)
end
metaclass.instance_eval do
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 0beb4321a2..bc4cca7855 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -284,7 +284,7 @@ module ActiveRecord
# })
#
# Will update the name of the Person with ID 1, build a new associated
- # person with the name `John', and mark the associatied Person with ID 2
+ # person with the name `John', and mark the associated Person with ID 2
# for destruction.
#
# Also accepts an Array of attribute hashes:
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index 253fa03785..4e172bd2b6 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -71,6 +71,21 @@ module ActiveRecord #:nodoc:
# </account>
# </firm>
#
+ # Additionally, the record being serialized will be passed to a Proc's second
+ # parameter. This allows for ad hoc additions to the resultant document that
+ # incorporate the context of the record being serialized. And by leveraging the
+ # closure created by a Proc, to_xml can be used to add elements that normally fall
+ # outside of the scope of the model -- for example, generating and appending URLs
+ # associated with models.
+ #
+ # proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
+ # firm.to_xml :procs => [ proc ]
+ #
+ # <firm>
+ # # ... normal attributes as shown above ...
+ # <name-reverse>slangis73</name-reverse>
+ # </firm>
+ #
# To include deeper levels of associations pass a hash like this:
#
# firm.to_xml :include => {:account => {}, :clients => {:include => :address}}
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 7ac6f6fe3b..a7fa98756e 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -63,7 +63,7 @@ module ActiveRecord
# default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
# translated attribute name and the value are available for interpolation.
#
- # When using inheritence in your models, it will check all the inherited models too, but only if the model itself
+ # When using inheritance in your models, it will check all the inherited models too, but only if the model itself
# hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
# error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
#
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 183be1e2f9..168b617fbc 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -74,10 +74,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_primary_key_implemented
- assert Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
- end
-
def test_defined_kernel_methods_implemented_in_model
%w(test name display y).each do |method|
klass = Class.new ActiveRecord::Base
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 3143ec2850..6bfe05b4fd 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -422,11 +422,6 @@ class BasicsTest < ActiveRecord::TestCase
end
- def test_reader_for_invalid_column_names
- Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
- assert !Topic.generated_methods.include?("mumub-jumbo")
- end
-
def test_non_attribute_access_and_assignment
topic = Topic.new
assert !topic.respond_to?("mumbo")
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 10daff5d65..3cb6b01e80 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -354,10 +354,6 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.to_a.uniq
end
- def test_methods_invoked_within_scopes_should_respect_scope
- assert_equal [], Topic.approved.by_rejected_ids.proxy_options[:conditions][:id]
- end
-
def test_named_scopes_batch_finders
assert_equal 3, Topic.approved.count
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index b49997669e..e1ad5c1685 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -174,6 +174,12 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
assert_match %r{<nationality>Danish</nationality>}, xml
end
+ def test_dual_arity_procs_are_called_on_object
+ proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
+ xml = authors(:david).to_xml(:procs => [ proc ])
+ assert_match %r{<name-reverse>divaD</name-reverse>}, xml
+ end
+
def test_top_level_procs_arent_applied_to_associations
author_proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
xml = authors(:david).to_xml(:procs => [ author_proc ], :include => :posts, :indent => 2)
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 201d96dcd7..9594dc300a 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -35,8 +35,6 @@ class Topic < ActiveRecord::Base
end
named_scope :named_extension, :extend => NamedExtension
named_scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne]
-
- named_scope :by_rejected_ids, lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }}
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/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb
index ee66479dde..dfce507b33 100644
--- a/activesupport/lib/active_support/buffered_logger.rb
+++ b/activesupport/lib/active_support/buffered_logger.rb
@@ -68,6 +68,10 @@ module ActiveSupport
message
end
+ # Dynamically add methods such as:
+ # def info
+ # def warn
+ # def debug
for severity in Severity.constants
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 954d0f5423..96a8000778 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -12,7 +12,7 @@ module ActiveSupport
# and MemCacheStore will load balance between all available servers. If a
# server goes down, then MemCacheStore will ignore it until it goes back
# online.
- # - Time-based expiry support. See #write and the +:expires_in+ option.
+ # - Time-based expiry support. See #write and the <tt>:expires_in</tt> option.
# - Per-request in memory cache for all communication with the MemCache server(s).
class MemCacheStore < Store
module Response # :nodoc:
@@ -64,9 +64,9 @@ module ActiveSupport
# Writes a value to the cache.
#
# Possible options:
- # - +:unless_exist+ - set to true if you don't want to update the cache
+ # - <tt>:unless_exist</tt> - set to true if you don't want to update the cache
# if the key is already set.
- # - +:expires_in+ - the number of seconds that this value may stay in
+ # - <tt>:expires_in</tt> - the number of seconds that this value may stay in
# the cache. See ActiveSupport::Cache::Store#write for an example.
def write(key, value, options = nil)
super
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index f049189b9a..238d1b6804 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -237,7 +237,7 @@ module ActiveSupport
# * the result from the callback
# * the object which has the callback
#
- # If the result from the block evaluates to false, the callback chain is stopped.
+ # If the result from the block evaluates to +true+, the callback chain is stopped.
#
# Example:
# class Storage
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 5f1ce4142f..11846f265c 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -12,7 +12,7 @@ class Array
default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale])
default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
- # Try to emulate to_senteces previous to 2.3
+ # Try to emulate to_sentences previous to 2.3
if options.has_key?(:connector) || options.has_key?(:skip_last_comma)
::ActiveSupport::Deprecation.warn(":connector has been deprecated. Use :words_connector instead", caller) if options.has_key? :connector
::ActiveSupport::Deprecation.warn(":skip_last_comma has been deprecated. Use :last_word_connector instead", caller) if options.has_key? :skip_last_comma
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 8309b617ae..434a32b29b 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -42,7 +42,7 @@ module Enumerable
#
# The latter is a shortcut for:
#
- # payments.inject { |sum, p| sum + p.price }
+ # payments.inject(0) { |sum, p| sum + p.price }
#
# It can also calculate the sum without the use of a block.
#
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index 8f49ddfd9e..1722726ca2 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -1,6 +1,6 @@
class Object
# Can you safely .dup this object?
- # False for nil, false, true, symbols, and numbers; true otherwise.
+ # False for nil, false, true, symbols, numbers, and class objects; true otherwise.
def duplicable?
true
end
diff --git a/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb b/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb
index a1c8ece1d7..9de8157eb0 100644
--- a/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb
+++ b/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb
@@ -1,5 +1,5 @@
# Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are
-# unmarshaled in the local zone, instead of utc. We're layering behavior on the _dump and _load
+# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load
# methods so that utc instances can be flagged on dump, and coerced back to utc on load.
if RUBY_VERSION < '1.9'
class Time
diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb
index 92c1de057b..4ee96b13b4 100644
--- a/activesupport/lib/active_support/inflector.rb
+++ b/activesupport/lib/active_support/inflector.rb
@@ -155,7 +155,7 @@ module ActiveSupport
# Examples:
# "posts".singularize # => "post"
# "octopi".singularize # => "octopus"
- # "sheep".singluarize # => "sheep"
+ # "sheep".singularize # => "sheep"
# "word".singularize # => "word"
# "CamelOctopi".singularize # => "CamelOctopus"
def singularize(word)
@@ -261,9 +261,9 @@ module ActiveSupport
# <%= link_to(@person.name, person_path(@person)) %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize(string, sep = '-')
- # replace accented chars with ther ascii equivalents
+ # replace accented chars with their ascii equivalents
parameterized_string = transliterate(string)
- # Turn unwanted chars into the seperator
+ # Turn unwanted chars into the separator
parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep)
unless sep.blank?
re_sep = Regexp.escape(sep)
diff --git a/activesupport/lib/active_support/mini.rb b/activesupport/lib/active_support/mini.rb
index fe7ba48e58..b787650655 100644
--- a/activesupport/lib/active_support/mini.rb
+++ b/activesupport/lib/active_support/mini.rb
@@ -1,9 +1,9 @@
$LOAD_PATH.unshift File.dirname(__FILE__)
-require "core_ext/blank"
# whole object.rb pulls up rarely used introspection extensions
+require "core_ext/object/blank"
require "core_ext/object/metaclass"
require 'core_ext/array'
require 'core_ext/hash'
require 'core_ext/module/attribute_accessors'
-require 'core_ext/string/inflections' \ No newline at end of file
+require 'core_ext/string/inflections'
diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb
index 9a0441206b..b65eeaaae7 100644
--- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb
+++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb
@@ -1,56 +1,56 @@
-#--
-# Copyright (c) 2008 Philip Ross
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
-require 'date'
-require 'rational'
-
-module TZInfo
-
- # Methods to support different versions of Ruby.
- module RubyCoreSupport #:nodoc:
-
- # Use Rational.new! for performance reasons in Ruby 1.8.
- # This has been removed from 1.9, but Rational performs better.
- if Rational.respond_to? :new!
- def self.rational_new!(numerator, denominator = 1)
- Rational.new!(numerator, denominator)
- end
- else
- def self.rational_new!(numerator, denominator = 1)
- Rational(numerator, denominator)
- end
- end
-
- # Ruby 1.8.6 introduced new! and deprecated new0.
- # Ruby 1.9.0 removed new0.
- # We still need to support new0 for older versions of Ruby.
- if DateTime.respond_to? :new!
- def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
- DateTime.new!(ajd, of, sg)
- end
- else
- def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
- DateTime.new0(ajd, of, sg)
- end
- end
- end
+#--
+# Copyright (c) 2008 Philip Ross
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#++
+
+require 'date'
+require 'rational'
+
+module TZInfo
+
+ # Methods to support different versions of Ruby.
+ module RubyCoreSupport #:nodoc:
+
+ # Use Rational.new! for performance reasons in Ruby 1.8.
+ # This has been removed from 1.9, but Rational performs better.
+ if Rational.respond_to? :new!
+ def self.rational_new!(numerator, denominator = 1)
+ Rational.new!(numerator, denominator)
+ end
+ else
+ def self.rational_new!(numerator, denominator = 1)
+ Rational(numerator, denominator)
+ end
+ end
+
+ # Ruby 1.8.6 introduced new! and deprecated new0.
+ # Ruby 1.9.0 removed new0.
+ # We still need to support new0 for older versions of Ruby.
+ if DateTime.respond_to? :new!
+ def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
+ DateTime.new!(ajd, of, sg)
+ end
+ else
+ def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
+ DateTime.new0(ajd, of, sg)
+ end
+ end
+ end
end \ No newline at end of file
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 1cd714d864..5c3d93c4a1 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -78,7 +78,7 @@ module ActiveSupport
# Merge all the texts of an element into the hash
#
# hash::
- # Hash to add the converted emement to.
+ # Hash to add the converted element to.
# element::
# XML element whose texts are to me merged into the hash
def merge_texts!(hash, element)
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 622523a1b9..847ab0152b 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -59,7 +59,7 @@ module ActiveSupport
memo[name] = child_hash
end
- # Recusively walk children
+ # Recursively walk children
child.children.each { |c|
callback.call(child_hash, child, c, callback)
}
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index aa2461535b..aaf4bb6bfd 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -60,7 +60,7 @@ module ActiveSupport
# Merge all the texts of an element into the hash
#
# hash::
- # Hash to add the converted emement to.
+ # Hash to add the converted element to.
# element::
# XML element whose texts are to me merged into the hash
def merge_texts!(hash, element)
diff --git a/railties/README b/railties/README
index 37ec8ea211..221e8486d1 100644
--- a/railties/README
+++ b/railties/README
@@ -28,7 +28,7 @@ link:files/vendor/rails/actionpack/README.html.
== Getting Started
1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
- and your application name. Ex: rails myapp
+ and your application name. Ex: <tt>rails myapp</tt>
2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!"
4. Follow the guidelines to start developing your application
@@ -36,61 +36,60 @@ link:files/vendor/rails/actionpack/README.html.
== Web Servers
-By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails
-with a variety of other web servers.
+By default, Rails will try to use Mongrel if it's installed when started with script/server, otherwise
+Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails with a variety of
+other web servers.
Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
More info at: http://mongrel.rubyforge.org
-Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or
-Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use
-FCGI or proxy to a pack of Mongrels/Thin/Ebb servers.
+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.
+
+For production use, often a web/proxy server such as Apache, LiteSpeed, Lighttpd or IIS 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).
+
== Apache .htaccess example for FCGI/CGI
-# General Apache options
-AddHandler fastcgi-script .fcgi
-AddHandler cgi-script .cgi
-Options +FollowSymLinks +ExecCGI
-
-# If you don't want Rails to look in certain directories,
-# use the following rewrite rules so that Apache won't rewrite certain requests
-#
-# Example:
-# RewriteCond %{REQUEST_URI} ^/notrails.*
-# RewriteRule .* - [L]
-
-# Redirect all requests not available on the filesystem to Rails
-# By default the cgi dispatcher is used which is very slow
-#
-# For better performance replace the dispatcher with the fastcgi one
-#
-# Example:
-# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
-RewriteEngine On
-
-# If your Rails application is accessed via an Alias directive,
-# then you MUST also set the RewriteBase in this htaccess file.
-#
-# Example:
-# Alias /myrailsapp /path/to/myrailsapp/public
-# RewriteBase /myrailsapp
-
-RewriteRule ^$ index.html [QSA]
-RewriteRule ^([^.]+)$ $1.html [QSA]
-RewriteCond %{REQUEST_FILENAME} !-f
-RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
-
-# In case Rails experiences terminal errors
-# Instead of displaying this message you can supply a file here which will be rendered instead
-#
-# Example:
-# ErrorDocument 500 /500.html
-
-ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
+General Apache options
+
+ AddHandler fastcgi-script .fcgi
+ AddHandler cgi-script .cgi
+ Options +FollowSymLinks +ExecCGI
+
+If you don't want Rails to look in certain directories, use the following
+rewrite rules so that Apache won't rewrite certain requests.
+
+ RewriteCond %{REQUEST_URI} ^/notrails.*
+ RewriteRule .* - [L]
+
+Redirect all requests not available on the filesystem to Rails. By default the
+cgi dispatcher is used which is very slow, for better performance replace the
+dispatcher with the fastcgi one.
+
+ RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+ RewriteEngine On
+
+If your Rails application is accessed via an Alias directive, then you MUST also
+set the RewriteBase in this htaccess file.
+
+ Alias /myrailsapp /path/to/myrailsapp/public
+ RewriteBase /myrailsapp
+
+ RewriteRule ^$ index.html [QSA]
+ RewriteRule ^([^.]+)$ $1.html [QSA]
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
+
+Incase Rails experiences terminal errors instead of displaying those messages you
+can supply a file here which will be rendered instead.
+ ErrorDocument 500 /500.html
+ ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
== Debugging Rails
diff --git a/railties/Rakefile b/railties/Rakefile
index 61c094150a..3212bf3a4f 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -26,7 +26,8 @@ task :default => :test
## below passes. It's not ideal, but at least
## we can see the failures
task :test do
- Dir['test/**/*_test.rb'].all? do |file|
+ dir = ENV["TEST_DIR"] || "**"
+ Dir["test/#{dir}/*_test.rb"].all? do |file|
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
system(ruby, '-Itest', "-I#{File.dirname(__FILE__)}/../activesupport/lib", file)
end or raise "Failures"
@@ -40,56 +41,22 @@ Rake::TestTask.new("regular_test") do |t|
t.verbose = true
end
-
-BASE_DIRS = %w(
- app
- config/environments
- config/initializers
- config/locales
- db
- doc
- log
- lib
- lib/tasks
- public
- script
- script/performance
- test
- vendor
- vendor/plugins
- tmp/sessions
- tmp/cache
- tmp/sockets
- tmp/pids
-)
-
-APP_DIRS = %w( models controllers helpers views views/layouts )
-PUBLIC_DIRS = %w( images javascripts stylesheets )
-TEST_DIRS = %w( fixtures unit functional mocks mocks/development mocks/test )
-
-LOG_FILES = %w( server.log development.log test.log production.log )
-HTML_FILES = %w( 422.html 404.html 500.html index.html robots.txt favicon.ico images/rails.png
- javascripts/prototype.js javascripts/application.js
- javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js )
-BIN_FILES = %w( about console destroy generate performance/benchmarker performance/profiler runner server plugin )
-
VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport activeresource railties )
-
desc "Generates a fresh Rails package with documentation"
-task :fresh_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content, :generate_documentation ]
+task :fresh_rails => [ :clean, :create_rails, :copy_vendor_libraries, :generate_documentation ]
desc "Generates a fresh Rails package using GEMs with documentation"
-task :fresh_gem_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_ties_content, :copy_gem_environment ]
+task :fresh_gem_rails => [ :clean, :create_rails ]
desc "Generates a fresh Rails package without documentation (faster)"
-task :fresh_rails_without_docs => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content ]
+task :fresh_rails_without_docs => [ :clean, :create_rails, :copy_vendor_libraries ]
-desc "Generates a fresh Rails package without documentation (faster)"
-task :fresh_rails_without_docs_using_links => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ]
+desc "Generates a fresh Rails package without documentation using links (faster)"
+task :fresh_rails_without_docs_using_links => [ :clean, :create_rails, :link_vendor_libraries ]
desc "Generates minimal Rails package using symlinks"
-task :dev => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ]
+task :dev => [ :clean, :create_rails, :link_vendor_libraries ]
desc "Packages the fresh Rails package with documentation"
task :package => [ :clean, :fresh_rails ] do
@@ -101,54 +68,31 @@ task :clean do
rm_rf PKG_DESTINATION
end
-# Get external spinoffs -------------------------------------------------------------------
+# Update spinoffs -------------------------------------------------------------------
-desc "Updates railties to the latest version of the javascript spinoffs"
-task :update_js do
- for js in %w( prototype controls dragdrop effects )
- rm "html/javascripts/#{js}.js"
- cp "./../actionpack/lib/action_view/helpers/javascripts/#{js}.js", "html/javascripts"
- end
+desc "Updates application README to the latest version Railties README"
+task :update_readme do
+ readme = "lib/generators/rails/app/templates/README"
+ rm readme
+ cp "./README", readme
end
-# Make directory structure ----------------------------------------------------------------
-
-def make_dest_dirs(dirs, path = '.')
- mkdir_p dirs.map { |dir| File.join(PKG_DESTINATION, path.to_s, dir) }
-end
-
-desc "Make the directory structure for the new Rails application"
-task :make_dir_structure => [ :make_base_dirs, :make_app_dirs, :make_public_dirs, :make_test_dirs ]
-
-task(:make_base_dirs) { make_dest_dirs BASE_DIRS }
-task(:make_app_dirs) { make_dest_dirs APP_DIRS, 'app' }
-task(:make_public_dirs) { make_dest_dirs PUBLIC_DIRS, 'public' }
-task(:make_test_dirs) { make_dest_dirs TEST_DIRS, 'test' }
-
-
-# Initialize file stubs -------------------------------------------------------------------
-
-desc "Initialize empty file stubs (such as for logging)"
-task :initialize_file_stubs => [ :initialize_log_files ]
+# Run application generator -------------------------------------------------------------
-task :initialize_log_files do
- log_dir = File.join(PKG_DESTINATION, 'log')
- chmod 0777, log_dir
- LOG_FILES.each do |log_file|
- log_path = File.join(log_dir, log_file)
- touch log_path
- chmod 0666, log_path
- end
+task :create_rails do
+ require File.join(File.dirname(__FILE__), 'lib', 'generators')
+ require 'generators/rails/app/app_generator'
+ Rails::Generators::AppGenerator.start [ File.basename(PKG_DESTINATION), "--quiet" ],
+ :destination_root => File.expand_path(File.dirname(PKG_DESTINATION))
end
-
# Copy Vendors ----------------------------------------------------------------------------
desc "Copy in all the Rails packages to vendor"
task :copy_vendor_libraries do
mkdir File.join(PKG_DESTINATION, 'vendor', 'rails')
VENDOR_LIBS.each { |dir| cp_r File.join('..', dir), File.join(PKG_DESTINATION, 'vendor', 'rails', dir) }
- FileUtils.rm_r(Dir.glob(File.join(PKG_DESTINATION, 'vendor', 'rails', "**", ".svn")))
+ FileUtils.rm_r(Dir.glob(File.join(PKG_DESTINATION, 'vendor', 'rails', "**", ".git")))
end
desc "Link in all the Rails packages to vendor"
@@ -158,96 +102,6 @@ task :link_vendor_libraries do
end
-# Copy Ties Content -----------------------------------------------------------------------
-
-desc "Make copies of all the default content of ties"
-task :copy_ties_content => [
- :copy_rootfiles, :copy_dispatches, :copy_html_files, :copy_application,
- :copy_configs, :copy_binfiles, :copy_test_helpers, :copy_app_doc_readme ]
-
-task :copy_dispatches do
- copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.rb")
- chmod 0755, "#{PKG_DESTINATION}/public/dispatch.rb"
-
- copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.cgi")
- chmod 0755, "#{PKG_DESTINATION}/public/dispatch.cgi"
-
- copy_with_rewritten_ruby_path("dispatches/dispatch.fcgi", "#{PKG_DESTINATION}/public/dispatch.fcgi")
- chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi"
-end
-
-task :copy_html_files do
- HTML_FILES.each { |file| cp File.join('html', file), File.join(PKG_DESTINATION, 'public', file) }
-end
-
-task :copy_application do
- cp "helpers/application_controller.rb", "#{PKG_DESTINATION}/app/controllers/application_controller.rb"
- cp "helpers/application_helper.rb", "#{PKG_DESTINATION}/app/helpers/application_helper.rb"
-end
-
-task :copy_configs do
- app_name = "rails"
- socket = nil
- require 'erb'
- File.open("#{PKG_DESTINATION}/config/database.yml", 'w') {|f| f.write ERB.new(IO.read("configs/databases/sqlite3.yml"), nil, '-').result(binding)}
-
- cp "configs/routes.rb", "#{PKG_DESTINATION}/config/routes.rb"
-
- cp "configs/initializers/backtrace_silencers.rb", "#{PKG_DESTINATION}/config/initializers/backtrace_silencers.rb"
- cp "configs/initializers/inflections.rb", "#{PKG_DESTINATION}/config/initializers/inflections.rb"
- cp "configs/initializers/mime_types.rb", "#{PKG_DESTINATION}/config/initializers/mime_types.rb"
- cp "configs/initializers/new_rails_defaults.rb", "#{PKG_DESTINATION}/config/initializers/new_rails_defaults.rb"
-
- cp "configs/locales/en.yml", "#{PKG_DESTINATION}/config/locales/en.yml"
-
- cp "configs/seeds.rb", "#{PKG_DESTINATION}/db/seeds.rb"
-
- cp "environments/boot.rb", "#{PKG_DESTINATION}/config/boot.rb"
- cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb"
- cp "environments/production.rb", "#{PKG_DESTINATION}/config/environments/production.rb"
- cp "environments/development.rb", "#{PKG_DESTINATION}/config/environments/development.rb"
- cp "environments/test.rb", "#{PKG_DESTINATION}/config/environments/test.rb"
-
-end
-
-task :copy_binfiles do
- BIN_FILES.each do |file|
- dest_file = File.join(PKG_DESTINATION, 'script', file)
- copy_with_rewritten_ruby_path(File.join('bin', file), dest_file)
- chmod 0755, dest_file
- end
-end
-
-task :copy_rootfiles do
- cp "fresh_rakefile", "#{PKG_DESTINATION}/Rakefile"
- cp "README", "#{PKG_DESTINATION}/README"
- cp "CHANGELOG", "#{PKG_DESTINATION}/CHANGELOG"
-end
-
-task :copy_test_helpers do
- cp "helpers/test_helper.rb", "#{PKG_DESTINATION}/test/test_helper.rb"
-end
-
-task :copy_app_doc_readme do
- cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP"
-end
-
-def copy_with_rewritten_ruby_path(src_file, dest_file)
- ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
-
- File.open(dest_file, 'w') do |df|
- File.open(src_file) do |sf|
- line = sf.gets
- if (line =~ /#!.+ruby\s*/) != nil
- df.puts("#!#{ruby}")
- else
- df.puts(line)
- end
- df.write(sf.read)
- end
- end
-end
-
desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"'
task :generate_guides do
ENV["WARN_BROKEN_LINKS"] = "1" # authors can't disable this
@@ -265,7 +119,6 @@ task :generate_rails_framework_doc do
end
task :generate_app_doc do
- cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP"
system %{cd #{PKG_DESTINATION}; rake doc:app}
end
@@ -278,30 +131,17 @@ Rake::RDocTask.new { |rdoc|
rdoc.rdoc_files.include('README', 'CHANGELOG')
rdoc.rdoc_files.include('lib/*.rb')
rdoc.rdoc_files.include('lib/rails/*.rb')
- rdoc.rdoc_files.include('lib/rails_generator/*.rb')
+ rdoc.rdoc_files.include('lib/generators/*.rb')
rdoc.rdoc_files.include('lib/commands/**/*.rb')
}
# Generate GEM ----------------------------------------------------------------------------
-task :copy_gem_environment do
- cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb"
- chmod 0755, dest_file
-end
-
-
PKG_FILES = FileList[
'[a-zA-Z]*',
'bin/**/*',
'builtin/**/*',
- 'configs/**/*',
- 'doc/**/*',
- 'dispatches/**/*',
- 'environments/**/*',
'guides/**/*',
- 'helpers/**/*',
- 'generators/**/*',
- 'html/**/*',
'lib/**/*'
] - [ 'test' ]
diff --git a/railties/bin/rails b/railties/bin/rails
index 6a0c675206..538d0cbc84 100755
--- a/railties/bin/rails
+++ b/railties/bin/rails
@@ -7,14 +7,9 @@ if %w(--version -v).include? ARGV.first
exit(0)
end
-freeze = ARGV.any? { |option| %w(--freeze -f).include?(option) }
+ARGV << "--help" if ARGV.empty?
-app_path = ARGV.first
+require File.dirname(__FILE__) + '/../lib/generators'
+require 'generators/rails/app/app_generator'
-require File.dirname(__FILE__) + '/../lib/rails_generator'
-
-require 'rails_generator/scripts/generate'
-Rails::Generator::Base.use_application_sources!
-Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app')
-
-Dir.chdir(app_path) { `rake rails:freeze:gems`; puts "froze" } if freeze \ No newline at end of file
+Rails::Generators::AppGenerator.start
diff --git a/railties/guides/files/javascripts/guides.js b/railties/guides/files/javascripts/guides.js
index 81fc07e799..c4e4d459ea 100755
--- a/railties/guides/files/javascripts/guides.js
+++ b/railties/guides/files/javascripts/guides.js
@@ -1,5 +1,4 @@
function guideMenu(){
-
if (document.getElementById('guides').style.display == "none") {
document.getElementById('guides').style.display = "block";
} else {
diff --git a/railties/guides/images/fxn.jpg b/railties/guides/images/fxn.jpg
deleted file mode 100644
index 81999341f1..0000000000
--- a/railties/guides/images/fxn.jpg
+++ /dev/null
Binary files differ
diff --git a/railties/guides/images/fxn.png b/railties/guides/images/fxn.png
new file mode 100644
index 0000000000..9b531ee584
--- /dev/null
+++ b/railties/guides/images/fxn.png
Binary files differ
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index 054ca99985..756caea5fe 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -41,7 +41,7 @@ def new
end
</ruby>
-The "Layouts & rendering guide":layouts_and_rendering.html explains this in more detail.
+The "Layouts & Rendering Guide":layouts_and_rendering.html explains this in more detail.
+ApplicationController+ inherits from +ActionController::Base+, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.
@@ -191,7 +191,7 @@ Session values are stored using key/value pairs like a hash:
<ruby>
class ApplicationController < ActionController::Base
-private
+ private
# Finds the User with the ID stored in the session with the key
# :current_user_id This is a common way to handle user login in
@@ -350,7 +350,8 @@ Before filters may halt the request cycle. A common before filter is one which r
class ApplicationController < ActionController::Base
before_filter :require_login
-private
+ private
+
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
@@ -374,7 +375,7 @@ The method simply stores an error message in the flash and redirects to the logi
In this example the filter is added to +ApplicationController+ and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with +skip_before_filter+:
<ruby>
-class LoginsController < Application
+class LoginsController < ApplicationController
skip_before_filter :require_login, :only => [:new, :create]
end
</ruby>
@@ -390,10 +391,11 @@ Around filters are responsible for running the action, but they can choose not t
<ruby>
# Example taken from the Rails API filter documentation:
# http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html
-class ApplicationController < Application
+class ApplicationController < ActionController::Base
around_filter :catch_exceptions
-private
+ private
+
def catch_exceptions
yield
rescue => exception
@@ -442,7 +444,7 @@ The Rails API documentation has "more information on using filters":http://api.r
h3. Verification
-Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHTTPRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter".
+Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHttpRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter".
Here's an example of using verification to make sure the user supplies a username and a password in order to log in:
@@ -575,7 +577,8 @@ class AdminController < ApplicationController
before_filter :authenticate
-private
+ private
+
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == USERNAME &&
@@ -597,7 +600,8 @@ class AdminController < ApplicationController
before_filter :authenticate
-private
+ private
+
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
@@ -626,7 +630,7 @@ class ClientsController < ApplicationController
:type => "application/pdf")
end
-private
+ private
def generate_pdf(client)
Prawn::Document.new do
@@ -728,7 +732,8 @@ Here's how you can use +rescue_from+ to intercept all +ActiveRecord::RecordNotFo
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
-private
+ private
+
def record_not_found
render :text => "404 Not Found", :status => 404
end
@@ -741,7 +746,8 @@ Of course, this example is anything but elaborate and doesn't improve on the def
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, :with => :user_not_authorized
-private
+ private
+
def user_not_authorized
flash[:error] = "You don't have access to this section."
redirect_to :back
@@ -757,7 +763,8 @@ class ClientsController < ApplicationController
@client = Client.find(params[:id])
end
-private
+ private
+
# If the user is not authorized, just throw the exception.
def check_authorization
raise User::NotAuthorized unless current_user.admin?
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 9476635ae6..2e7f0e7fed 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -46,7 +46,7 @@ class UserMailer < ActionMailer::Base
from "My Awesome Site Notifications <notifications@example.com>"
subject "Welcome to My Awesome Site"
sent_on Time.now
- body {:user => user, :url => "http://example.com/login"}
+ body( {:user => user, :url => "http://example.com/login"})
end
end
</ruby>
@@ -137,9 +137,9 @@ Hence, if the method name starts with +deliver_+ followed by any combination of
h4. Complete List of Action Mailer User-Settable Attributes
-|bcc| The BCC addresses of the email|
+|bcc| The BCC addresses of the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
|body| The body of the email. 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 body of the message|
-|cc| The CC addresses for the email|
+|cc| The CC addresses for the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
|charset| The charset to use for the email. This defaults to the +default_charset+ specified for ActionMailer::Base.|
|content_type| The content type for the email. This defaults to "text/plain" but the filename may specify it|
|from| The from address of the email|
@@ -165,10 +165,11 @@ class UserMailer < ActionMailer::Base
from "My Awesome Site Notifications<notifications@example.com>"
subject "Welcome to My Awesome Site"
sent_on Time.now
- body {:user => user, :url => "http://example.com/login"}
+ body( {:user => user, :url => "http://example.com/login"})
content_type "text/html"
# use some_other_template.text.(html|plain).erb instead
template "some_other_template"
+ end
end
</ruby>
@@ -264,9 +265,9 @@ end
h4. Sending Multipart Emails with Attachments
-Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename. You must declare which template you are using for each content type via the +part+ method.
+Once you use the +attachment+ method, ActionMailer will no longer automagically use the correct template based on the filename, nor will it properly order the alternative parts. You must declare which template you are using for each content type via the +part+ method. And you must declare these templates in the proper order.
-In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder.
+In the following example, there would be two template files, +welcome_email_html.erb+ and +welcome_email_plain.erb+ in the +app/views/user_mailer+ folder. The +text/plain+ part must be listed first for full compatibility with email clients. If +text/plain+ is listed after +text/html+, some clients may display both the HTML and plain text versions of the email. The text alternatives alone must be enclosed in a +multipart/alternative+ part. Do not set the entire message's +content_type+ to +multipart/alternative+ or some email clients may ignore the display of attachments such as PDF's.
<ruby>
class UserMailer < ActionMailer::Base
@@ -274,14 +275,15 @@ class UserMailer < ActionMailer::Base
recipients user.email_address
subject "New account information"
from "system@example.com"
- content_type "multipart/alternative"
- part "text/html" do |p|
- p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>")
- end
+ part "multipart/alternative" do |pt|
+ pt.part "text/plain" do |p|
+ p.body = render_message("welcome_email_plain", :message => "text content")
+ end
- part "text/plain" do |p|
- p.body = render_message("welcome_email_plain", :message => "text content")
+ pt.part "text/html" do |p|
+ p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>")
+ end
end
attachment :content_type => "image/jpeg",
@@ -369,9 +371,9 @@ ActionMailer::Base.default_charset = "iso-8859-1"
h4. Action Mailer Configuration for GMail
-Instructions copied from http://http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html
+Instructions copied from "this blog entry":http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html by Robert Zotter.
-First you must install the +action_mailer_tls+ plugin from http://code.openrain.com/rails/action_mailer_tls/, then all you have to do is configure action mailer.
+First you must install the "action_mailer_tls":http://github.com/openrain/action_mailer_tls plugin, then all you have to do is configure Action Mailer:
<ruby>
ActionMailer::Base.smtp_settings = {
diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile
index afff892fd4..bf6e3c8181 100644
--- a/railties/guides/source/active_record_basics.textile
+++ b/railties/guides/source/active_record_basics.textile
@@ -69,7 +69,7 @@ h4. Schema Conventions
ActiveRecord uses naming conventions for the columns in database tables, depending on the purpose of these columns.
* *Foreign keys* - These fields should be named following the pattern table_id i.e. (item_id, order_id). These are the fields that ActiveRecord will look for when you create associations between your models.
-* *Primary keys* - By default, ActiveRecord will use a integer column named "id" as the table's primary key. When using "Rails Migrations":http://guides.rails.info/migrations.html to create your tables, this column will be automatically created.
+* *Primary keys* - By default, ActiveRecord will use an integer column named "id" as the table's primary key. When using "Rails Migrations":migrations.html to create your tables, this column will be automatically created.
There are also some optional column names that will create additional features to ActiveRecord instances:
@@ -127,9 +127,9 @@ end
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":http://guides.rails.info/activerecord_validations_callbacks.html#_overview_of_activerecord_validation.
+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.
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":http://guides.rails.info/activerecord_validations_callbacks.html#_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.
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index b112c4f5fb..302dad4f1a 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -6,7 +6,6 @@ This guide covers different ways to retrieve data from the database using Active
* Specify the order, retrieved attributes, grouping, and other properties of the found records
* Use eager loading to reduce the number of database queries needed for data retrieval
* Use dynamic finders methods
-* Create named scopes to add custom finding behavior to your models
* Check for the existence of particular records
* Perform various calculations on Active Record models
@@ -23,7 +22,6 @@ TIP: All of the following models uses +id+ as the primary key, unless specified
<ruby>
class Client < ActiveRecord::Base
has_one :address
- has_one :mailing_address
has_many :orders
has_and_belongs_to_many :roles
end
@@ -36,11 +34,6 @@ end
</ruby>
<ruby>
-class MailingAddress < Address
-end
-</ruby>
-
-<ruby>
class Order < ActiveRecord::Base
belongs_to :client, :counter_cache => true
end
@@ -245,7 +238,7 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t
h4. Array Conditions
-Now what if that number could vary, say as a argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like:
+Now what if that number could vary, say as an argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like:
<ruby>
Client.first(:conditions => ["orders_count = ?", params[:orders]])
@@ -465,7 +458,7 @@ Be careful because this also means you're initializing a model object with only
ActiveRecord::MissingAttributeError: missing attribute: <attribute>
</shell>
-Where +<attribute>+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
+Where +&lt;attribute&gt;+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the +DISTINCT+ function you can do it like this:
@@ -539,7 +532,7 @@ This will return single order objects for each day, but only for the last month.
h4. Readonly Objects
-To explicitly disallow modification/destroyal of the matching records returned by +Model.find+, you could specify the +:readonly+ option as +true+ to the find call.
+To explicitly disallow modification/destruction of the matching records returned by +Model.find+, you could specify the +:readonly+ option as +true+ to the find call.
Any attempt to alter or destroy the readonly records will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception. To set this option, specify it like this:
@@ -807,12 +800,12 @@ For every field (also known as an attribute) you define in your table, Active Re
You can do +find_last_by_*+ methods too which will find the last record matching your argument.
-You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+
+You can specify an exclamation point (<tt>!</tt>) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+
If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked("Ryan", true)+.
-There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+:
+There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+:
<sql>
SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1
diff --git a/railties/guides/source/active_support_overview.textile b/railties/guides/source/active_support_overview.textile
new file mode 100644
index 0000000000..aea77c8d4e
--- /dev/null
+++ b/railties/guides/source/active_support_overview.textile
@@ -0,0 +1,818 @@
+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 5ae4884297..03d521ea1f 100644
--- a/railties/guides/source/activerecord_validations_callbacks.textile
+++ b/railties/guides/source/activerecord_validations_callbacks.textile
@@ -338,7 +338,7 @@ end
Besides +:only_integer+, the +validates_numericality_of+ helper also accepts the following options to add constraints to acceptable values:
* +:greater_than+ - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than {{count}}_".
-* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}".
+* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}_".
* +:equal_to+ - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to {{count}}_".
* +:less_than+ - Specifies the value must be less than the supplied value. The default error message for this option is "_must be less than {{count}}_".
* +:less_than_or_equal_to+ - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less or equal to {{count}}_".
@@ -530,11 +530,11 @@ class Invoice < ActiveRecord::Base
end
</ruby>
-You can even create your own validation helpers and reuse them in several different models. Here is an example where we create a custom validation helper to validate the format of fields that represent email addresses:
+You can even create your own validation helpers and reuse them in several different models. For example, an application that manages surveys may find useful to express that a certain field corresponds to a set of choices:
<ruby>
ActiveRecord::Base.class_eval do
- def self.validates_as_radio(attr_name, n, options={})
+ def self.validates_as_choice(attr_name, n, options={})
validates_inclusion_of attr_name, {:in => 1..n}.merge(options)
end
end
@@ -544,7 +544,7 @@ Simply reopen +ActiveRecord::Base+ and define a class method like that. You'd ty
<ruby>
class Movie < ActiveRecord::Base
- validates_as_radio :rating, 5
+ validates_as_choice :rating, 5
end
</ruby>
diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile
new file mode 100644
index 0000000000..74e8dec5cf
--- /dev/null
+++ b/railties/guides/source/ajax_on_rails.textile
@@ -0,0 +1,94 @@
+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
+* Testing Javascript functionality
+* Becoming an Ajax Master on Rails: Plugins, Best Practices, Tips and Tricks
+
+endprologue.
+
+h3. Hello AJAX - a Quick Intro
+
+If you are a 'show me the code' type of person, you might want to skip this part and jump to the RJS section right away. However, I would really recommend to read it - you'll need the basics of DOM, http requests and other topics discussed here to really understand Ajax on Rails.
+
+h4. Asynchronous Javascript + XML
+
+Basic terminology, new style of creating web apps
+
+h4. The DOM
+
+basics of the DOM, how is it built, properties, features, why is it central to AJAX
+
+h4. Standard HTML communication vs AJAX
+
+How do 'standard' and AJAX requests differ, why does this matter for understanding AJAX on Rails (tie in for *_remote helpers, the next section)
+
+
+
+
+
+
+h3. Built-in Rails Helpers
+
+Mostly a reference to standard JS helpers like link_to_remote, remote_form_for etc + some explanation
+
+
+
+h3. Responding to AJAX the Rails way: RJS
+
+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
+
+
+
+h3. I Want my Yellow Thingy: Prototype and Script.aculo.us
+
+Walk through prototype and script.aculo.us, most important functionality, method reference etc.
+
+
+
+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
+
+Note to self: check out the RailsConf JS testing video
+
+h3. Useful Plugins
+
+This was in the ticket description, but at the moment I don't really have clue what to add here, so please tell me
+
+
+
+h3. Tips and Tricks
+
+* Unobtrusive Javascript (Prototype events, maybe the jQuery way (esp. jQeury.live()))
+
+* 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
+
+* Using AJAX to load stuff asynchronously
+** To avoid page blocking
+** Tricking page caching
+*** inserting user-specific info into a cached page
+*** anti-CSFR bit
+
+* Jumping to the top? Try event.stopPropagation
+
+* 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
+
+* Last but not least: Javascript is your friend :)
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index 03e22bd6fe..ca10014ee0 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -30,7 +30,7 @@ Now, suppose we wanted to add a new order for an existing customer. We'd need to
Or consider deleting a customer, and ensuring that all of its orders get deleted as well:
<ruby>
-@orders = Order.find_by_customer_id(@customer.id)
+@orders = Order.find_all_by_customer_id(@customer.id)
@orders.each do |order|
order.destroy
end
@@ -600,6 +600,7 @@ The +belongs_to+ association supports these options:
* +:polymorphic+
* +:readonly+
* +:select+
+* +:touch+
* +:validate+
h6. +:autosave+
@@ -736,6 +737,28 @@ The +:select+ option lets you override the SQL +SELECT+ clause that is used to r
TIP: If you set the +:select+ option on a +belongs_to+ association, you should also set the +foreign_key+ option to guarantee the correct results.
+h6. +:touch+
+
+If you set the +:touch+ option to +:true+, then the +updated_at+ or +updated_on+ timestamp on the associated object will be set to the current time whenever this object is saved or destroyed:
+
+<ruby>
+class Order < ActiveRecord::Base
+ belongs_to :customer, :touch => true
+end
+
+class Customer < ActiveRecord::Base
+ has_many :orders
+end
+</ruby>
+
+In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update:
+
+<ruby>
+class Order < ActiveRecord::Base
+ belongs_to :customer, :touch => :orders_updated_at
+end
+</ruby>
+
h6. +:validate+
If you set the +:validate+ option to +true+, then associated objects will be validated whenever you save this object. By default, this is +false+: associated objects will not be validated when this object is saved.
@@ -996,7 +1019,7 @@ When you declare a +has_many+ association, the declaring class automatically gai
* <tt><em>collection</em>.empty?</tt>
* <tt><em>collection</em>.size</tt>
* <tt><em>collection</em>.find(...)</tt>
-* <tt><em>collection</em>.exist?(...)</tt>
+* <tt><em>collection</em>.exists?(...)</tt>
* <tt><em>collection</em>.build(attributes = {}, ...)</tt>
* <tt><em>collection</em>.create(attributes = {})</tt>
@@ -1021,7 +1044,7 @@ orders.clear
orders.empty?
orders.size
orders.find(...)
-orders.exist?(...)
+orders.exists?(...)
orders.build(attributes = {}, ...)
orders.create(attributes = {})
</ruby>
@@ -1099,9 +1122,9 @@ The <tt><em>collection</em>.find</tt> method finds objects within the collection
@open_orders = @customer.orders.find(:all, :conditions => "open = 1")
</ruby>
-h6. <tt><em>collection</em>.exist?(...)</tt>
+h6. <tt><em>collection</em>.exists?(...)</tt>
-The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
+The <tt><em>collection</em>.exists?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
h6. <tt><em>collection</em>.build(attributes = {}, ...)</tt>
@@ -1196,6 +1219,17 @@ end
If you use a hash-style +:conditions+ option, then record creation via this association will be automatically scoped using the hash. In this case, using +@customer.confirmed_orders.create+ or +@customer.confirmed_orders.build+ will create orders where the confirmed column has the value +true+.
+If you need to evaluate conditions dynamically at runtime, you could use string interpolation in single quotes:
+
+<ruby>
+class Customer < ActiveRecord::Base
+ has_many :latest_orders, :class_name => "Order",
+ :conditions => 'orders.created_at > #{10.hours.ago.to_s(:db).inspect}'
+end
+</ruby>
+
+Be sure to use single quotes.
+
h6. +:counter_sql+
Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
@@ -1361,7 +1395,7 @@ When you declare a +has_and_belongs_to_many+ association, the declaring class au
* <tt><em>collection</em>.empty?</tt>
* <tt><em>collection</em>.size</tt>
* <tt><em>collection</em>.find(...)</tt>
-* <tt><em>collection</em>.exist?(...)</tt>
+* <tt><em>collection</em>.exists?(...)</tt>
* <tt><em>collection</em>.build(attributes = {})</tt>
* <tt><em>collection</em>.create(attributes = {})</tt>
@@ -1386,7 +1420,7 @@ assemblies.clear
assemblies.empty?
assemblies.size
assemblies.find(...)
-assemblies.exist?(...)
+assemblies.exists?(...)
assemblies.build(attributes = {}, ...)
assemblies.create(attributes = {})
</ruby>
@@ -1471,9 +1505,9 @@ The <tt><em>collection</em>.find</tt> method finds objects within the collection
:conditions => ["created_at > ?", 2.days.ago])
</ruby>
-h6. <tt><em>collection</em>.exist?(...)</tt>
+h6. <tt><em>collection</em>.exists?(...)</tt>
-The <tt><em>collection</em>.exist?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
+The <tt><em>collection</em>.exists?</tt> method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as +ActiveRecord::Base.exists?+.
h6. <tt><em>collection</em>.build(attributes = {})</tt>
@@ -1775,6 +1809,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/11
+* April 19, 2009: Added +:touch+ option to +belongs_to+ associations by "Mike Gunderloy":credits.html#mgunderloy
* February 1, 2009: Added +:autosave+ option "Mike Gunderloy":credits.html#mgunderloy
* September 28, 2008: Corrected +has_many :through+ diagram, added polymorphic diagram, some reorganization by "Mike Gunderloy":credits.html#mgunderloy . First release version.
* September 22, 2008: Added diagrams, misc. cleanup by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index f1ad7b820d..2865bc504a 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -1,8 +1,6 @@
h2. Caching with Rails: An overview
-Everyone caches. This guide will teach you what you need to know about
-avoiding that expensive round-trip to your database and returning what you
-need to return to those hungry web clients in the shortest time possible.
+This guide will teach you what you need to know about avoiding that expensive round-trip to your database and returning what you need to return to the web clients in the shortest time possible.
After reading this guide, you should be able to use and configure:
@@ -15,14 +13,9 @@ endprologue.
h3. Basic Caching
-This is an introduction to the three types of caching techniques that Rails
-provides by default without the use of any third party plugins.
+This is an introduction to the three types of caching techniques that Rails provides by default without the use of any third party plugins.
-To start playing with testing you'll want to ensure that
-+config.action_controller.perform_caching+ is set
-to +true+ if you're running in development mode. This flag is normally set in the
-corresponding config/environments/*.rb and caching is disabled by default
- for development and test, and enabled for production.
+To start playing with testing you'll want to ensure that +config.action_controller.perform_caching+ is set to +true+ if you're running in development mode. This flag is normally set in the corresponding +config/environments/*.rb+ and caching is disabled by default for development and test, and enabled for production.
<ruby>
config.action_controller.perform_caching = true
@@ -30,16 +23,9 @@ config.action_controller.perform_caching = true
h4. Page Caching
-Page caching is a Rails mechanism which allows the request for a generated
-page to be fulfilled by the webserver (i.e. apache or nginx), without ever having to go through the
-Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be
-applied to every situation (such as pages that need authentication) and since
-the webserver is literally just serving a file from the filesystem, cache
-expiration is an issue that needs to be dealt with.
+Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with.
-So, how do you enable this super-fast cache behavior? Simple, let's say you
-have a controller called +ProductsController+ and an +index+ action that lists all
-the products
+So, how do you enable this super-fast cache behavior? Simple, let's say you have a controller called +ProductsController+ and an +index+ action that lists all the products
<ruby>
class ProductsController < ActionController
@@ -53,25 +39,13 @@ class ProductsController < ActionController
end
</ruby>
-The first time anyone requests +/products+, Rails will generate a file
-called +products.html+ and the webserver will then look for that file before it
-passes the next request for +/products+ to your Rails application.
+The first time anyone requests +/products+, Rails will generate a file called +products.html+ and the webserver will then look for that file before it passes the next request for +/products+ to your Rails application.
-By default, the page cache directory is set to +Rails.public_path+ (which is
-usually set to the +public+ folder) and this can be configured by
-changing the configuration setting +config.action_controller.page_cache_directory+.
-Changing the default from +public+ helps avoid naming conflicts, since you may
-want to put other static html in +public+, but changing this will require web
-server reconfiguration to let the web server know where to serve the cached
-files from.
+By default, the page cache directory is set to +Rails.public_path+ (which is usually set to the +public+ folder) and this can be configured by changing the configuration setting +config.action_controller.page_cache_directory+. Changing the default from +public+ helps avoid naming conflicts, since you may want to put other static html in +public+, but changing this will require web server reconfiguration to let the web server know where to serve the cached files from.
-The Page Caching mechanism will automatically add a +.html+ extension to
-requests for pages that do not have an extension to make it easy for the
-webserver to find those pages and this can be configured by changing the
-configuration setting +config.action_controller.page_cache_extension+.
+The Page Caching mechanism will automatically add a +.html+ extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages and this can be configured by changing the configuration setting +config.action_controller.page_cache_extension+.
-In order to expire this page when a new product is added we could extend our
-example controller like this:
+In order to expire this page when a new product is added we could extend our example controller like this:
<ruby>
class ProductsController < ActionController
@@ -83,26 +57,19 @@ class ProductsController < ActionController
end
def create
- expire_page :action => :index
+ expire_page :action => :list
end
end
</ruby>
-If you want a more complicated expiration scheme, you can use cache sweepers
-to expire cached objects when things change. This is covered in the section on Sweepers.
+If you want a more complicated expiration scheme, you can use cache sweepers to expire cached objects when things change. This is covered in the section on Sweepers.
Note: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL!
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.
+One of the issues with Page Caching is that you cannot use it for pages that require to restrict access somehow. This is where Action Caching comes in. Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy.
Clearing the cache works in the exact same way as with Page Caching.
@@ -125,37 +92,19 @@ class ProductsController < ActionController
end
</ruby>
-You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the
-action should be cached. Also, you can use +:layout => false+ to cache without
-layout so that dynamic information in the layout such as logged in user info
-or the number of items in the cart can be left uncached. This feature is
-available as of Rails 2.2.
+You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the action should be cached. Also, you can use +:layout => false+ to cache without layout so that dynamic information in the layout such as logged in user info or the number of items in the cart can be left uncached. This feature is available as of Rails 2.2.
-You can modify the default action cache path by passing a +:cache_path+ option.
-This will be passed directly to +ActionCachePath.path_for+. This is handy for
-actions with multiple possible routes that should be cached differently. If
-a block is given, it is called with the current controller instance.
+You can modify the default action cache path by passing a +:cache_path+ option. This will be passed directly to +ActionCachePath.path_for+. This is handy for actions with multiple possible routes that should be cached differently. If a block is given, it is called with the current controller instance.
-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.
+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.
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.
+Life would be perfect if we could get away with caching the entire contents of a page or action and serving it out to the world. Unfortunately, dynamic web applications usually build pages with a variety of components not all of which have the same caching characteristics. In order to address such a dynamically created page where different parts of the page need to be cached and expired differently Rails provides a mechanism called Fragment Caching.
-Fragment Caching allows a fragment of view logic to be wrapped in a cache
-block and served out of the cache store when the next request comes in.
+Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.
-As an example, if you wanted to show all the orders placed on your website
-in real time and didn't want to cache that part of the page, but did want
-to cache the part of the page which lists all products available, you
-could use this piece of code:
+As an example, if you wanted to show all the orders placed on your website in real time and didn't want to cache that part of the page, but did want to cache the part of the page which lists all products available, you could use this piece of code:
<ruby>
<% Order.find_recent.each do |o| %>
@@ -170,9 +119,7 @@ could use this piece of code:
<% end %>
</ruby>
-The cache block in our example will bind to the action that called it and is
-written out to the same place as the Action Cache, which means that if you
-want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call:
+The cache block in our example will bind to the action that called it and is written out to the same place as the Action Cache, which means that if you want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call:
<ruby>
<% cache(:action => 'recent', :action_suffix => 'all_products') do %>
@@ -185,9 +132,7 @@ and you can expire it using the +expire_fragment+ method, like so:
expire_fragment(:controller => 'products', :action => 'recent', :action_suffix => 'all_products')
</ruby>
-If you don't want the cache block to bind to the action that called it, You can
-also use globally keyed fragments by calling the +cache+ method with a key, like
-so:
+If you don't want the cache block to bind to the action that called it, You can also use globally keyed fragments by calling the +cache+ method with a key, like so:
<ruby>
<% cache('all_available_products') do %>
@@ -195,8 +140,7 @@ so:
<% end %>
</ruby>
-This fragment is then available to all actions in the +ProductsController+ using
-the key and can be expired the same way:
+This fragment is then available to all actions in the +ProductsController+ using the key and can be expired the same way:
<ruby>
expire_fragment('all_available_products')
@@ -204,15 +148,9 @@ expire_fragment('all_available_products')
h4. Sweepers
-Cache sweeping is a mechanism which allows you to get around having a ton of
-+expire_{page,action,fragment}+ calls in your code. It does this by moving all the work
-required to expire cached content into a +ActionController::Caching::Sweeper+
-class. This class is an Observer and looks for changes to an object via callbacks,
-and when a change occurs it expires the caches associated with that object in
-an around or after filter.
+Cache sweeping is a mechanism which allows you to get around having a ton of +expire_{page,action,fragment}+ calls in your code. It does this by moving all the work required to expire cached content into a +ActionController::Caching::Sweeper+ class. This class is an Observer and looks for changes to an object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter.
-Continuing with our Product controller example, we could rewrite it with a
-sweeper like this:
+Continuing with our Product controller example, we could rewrite it with a sweeper like this:
<ruby>
class ProductSweeper < ActionController::Caching::Sweeper
@@ -244,18 +182,13 @@ class ProductSweeper < ActionController::Caching::Sweeper
end
</ruby>
-You may notice that the actual product gets passed to the sweeper, so if we
-were caching the edit action for each product, we could add a expire method
-which specifies the page we want to expire:
+You may notice that the actual product gets passed to the sweeper, so if we were caching the edit action for each product, we could add an expire method which specifies the page we want to expire:
<ruby>
expire_action(:controller => 'products', :action => 'edit', :id => product)
</ruby>
-Then we add it to our controller to tell it to call the sweeper when certain
-actions are called. So, if we wanted to expire the cached content for the
-list and edit actions when the create action was called, we could do the
-following:
+Then we add it to our controller to tell it to call the sweeper when certain actions are called. So, if we wanted to expire the cached content for the list and edit actions when the create action was called, we could do the following:
<ruby>
class ProductsController < ActionController
@@ -273,10 +206,7 @@ end
h4. SQL Caching
-Query caching is a Rails feature that caches the result set returned by each
-query so that if Rails encounters the same query again for that request, it
-will used the cached result set as opposed to running the query against the
-database again.
+Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again.
For example:
@@ -296,131 +226,85 @@ class ProductsController < ActionController
end
</ruby>
-The second time the same query is run against the database, it's not actually
-going to hit the database. The first time the result is returned from the query
-it is stored in the query cache (in memory) and the second time it's pulled from memory.
+The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory.
-However, it's important to note that query caches are created at the start of an action and destroyed at the end of
-that action and thus persist only for the duration of the action. If you'd like to store query results in a more
-persistent fashion, you can in Rails by using low level caching.
+However, it's important to note that query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action. If you'd like to store query results in a more persistent fashion, you can in Rails by using low level caching.
-h4. Cache stores
+h3. Cache Stores
-Rails (as of 2.1) provides different stores for the cached data created by action and
-fragment caches. Page caches are always stored on disk.
+Rails provides different stores for the cached data created by action and fragment caches. Page caches are always stored on disk.
-Rails 2.1 and above provide +ActiveSupport::Cache::Store+ which can be used to
-cache strings. Some cache store implementations, like MemoryStore, are able to
-cache arbitrary Ruby objects, but don't count on every cache store to be able
-to do that.
+Rails 2.1 and above provide +ActiveSupport::Cache::Store+ which can be used to cache strings. Some cache store implementations, like +MemoryStore+, are able to cache arbitrary Ruby objects, but don't count on every cache store to be able to do that.
The default cache stores provided with Rails include:
-1) ActiveSupport::Cache::MemoryStore: A cache store implementation which stores
-everything into memory in the same process. If you're running multiple Ruby on
-Rails server processes (which is the case if you're using mongrel_cluster or
-Phusion Passenger), then this means that your Rails server process instances
-won't be able to share cache data with each other. If your application never
-performs manual cache item expiry (e.g. when you‘re using generational cache
-keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you
-should be using this cache store.
+1) +ActiveSupport::Cache::MemoryStore+: A cache store implementation which stores everything into memory in the same process. If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then this means that your Rails server process instances won't be able to share cache data with each other. If your application never performs manual cache item expiry (e.g. when you‘re using generational cache keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you should be using this cache store.
-+MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
++MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
-+MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you
-need thread-safety.
++MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you need thread-safety.
-
<ruby>
ActionController::Base.cache_store = :memory_store
</ruby>
-2) ActiveSupport::Cache::FileStore: Cached data is stored on the disk, this is
-the default store and the default path for this store is: /tmp/cache. Works
-well for all types of environments and allows all processes running from the
-same application directory to access the cached content. If /tmp/cache does not
-exist, the default store becomes MemoryStore.
-
+2) +ActiveSupport::Cache::FileStore+: Cached data is stored on the disk, this is the default store and the default path for this store is +tmp/cache+. Works well for all types of environments and allows all processes running from the same application directory to access the cached content. If +tmp/cache+ does not exist, the default store becomes +MemoryStore+.
<ruby>
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
</ruby>
-3) ActiveSupport::Cache::DRbStore: Cached data is stored in a separate shared
-DRb process that all servers communicate with. This works for all environments
-and only keeps one cache around for all processes, but requires that you run
-and manage a separate DRb process.
-
+3) +ActiveSupport::Cache::DRbStore+: Cached data is stored in a separate shared DRb process that all servers communicate with. This works for all environments and only keeps one cache around for all processes, but requires that you run and manage a separate DRb process.
<ruby>
ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
</ruby>
-4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
-Rails uses the bundled memcached-client gem by default. This is currently the
-most popular cache store for production websites.
+4) +ActiveSupport::Cache::MemCacheStore+: Works like +DRbStore+, but uses Danga's +memcached+ instead. Rails uses the bundled +memcached-client+ gem by default. This is currently the most popular cache store for production websites.
Special features:
- * Clustering and load balancing. One can specify multiple memcached servers,
- and MemCacheStore will load balance between all available servers. If a
- server goes down, then MemCacheStore will ignore it until it goes back
- online.
- * Time-based expiry support. See +write+ and the +:expires_in+ option.
- * Per-request in memory cache for all communication with the MemCache server(s).
-It also accepts a hash of additional options:
+* Clustering and load balancing. One can specify multiple memcached servers, and +MemCacheStore+ will load balance between all available servers. If a server goes down, then +MemCacheStore+ will ignore it until it goes back online.
+* Time-based expiry support. See +write+ and the +:expires_in+ option.
+* Per-request in memory cache for all communication with the +memcached+ server(s).
- * +:namespace+- specifies a string that will automatically be prepended to keys when accessing the memcached store.
- * +:readonly+- a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
- * +:multithread+ - a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality.
+It also accepts a hash of additional options:
-The read and write methods of the MemCacheStore accept an options hash too.
-When reading you can specify +:raw => true+ to prevent the object being marshaled
-(by default this is false which means the raw value in the cache is passed to
-+Marshal.load+ before being returned to you.)
+* +:namespace+: specifies a string that will automatically be prepended to keys when accessing the memcached store.
+* +:readonly+: a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
+* +:multithread+: a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality.
-When writing to the cache it is also possible to specify +:raw => true+ means
-the value is not passed to +Marshal.dump+ before being stored in the cache (by
-default this is false).
+The read and write methods of the +MemCacheStore+ accept an options hash too. When reading you can specify +:raw => true+ to prevent the object being marshaled (by default this is false which means the raw value in the cache is passed to +Marshal.load+ before being returned to you.)
-The write method also accepts an +:unless_exist+ flag which determines whether
-the memcached add (when true) or set (when false) method is used to store the
-item in the cache and an +:expires_in+ option that specifies the time-to-live
-for the cached item in seconds.
+When writing to the cache it is also possible to specify +:raw => true+ means the value is not passed to +Marshal.dump+ before being stored in the cache (by default this is false).
+The write method also accepts an +:unless_exist+ flag which determines whether the memcached add (when true) or set (when false) method is used to store the item in the cache and an +:expires_in+ option that specifies the time-to-live for the cached item in seconds.
<ruby>
ActionController::Base.cache_store = :mem_cache_store, "localhost"
</ruby>
-5) ActiveSupport::Cache::SynchronizedMemoryStore: Like ActiveSupport::Cache::MemoryStore but thread-safe.
-
+5) +ActiveSupport::Cache::SynchronizedMemoryStore+: Like +MemoryStore+ but thread-safe.
<ruby>
ActionController::Base.cache_store = :synchronized_memory_store
</ruby>
-6) ActiveSupport::Cache::CompressedMemCacheStore: Works just like the regular
-MemCacheStore but uses GZip to decompress/compress on read/write.
-
+6) +ActiveSupport::Cache::CompressedMemCacheStore+: Works just like the regular +MemCacheStore+ but uses GZip to decompress/compress on read/write.
<ruby>
ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"
</ruby>
-7) Custom store: You can define your own cache store (new in Rails 2.1)
-
+7) Custom store: You can define your own cache store (new in Rails 2.1).
<ruby>
ActionController::Base.cache_store = MyOwnStore.new("parameter")
</ruby>
-+Note: +config.cache_store+ can be used in place of
-+ActionController::Base.cache_store+ in your +Rails::Initializer.run+ block in
-+environment.rb+
+NOTE: +config.cache_store+ can be used in place of +ActionController::Base.cache_store+ in your +Rails::Initializer.run+ block in +environment.rb+
-In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+
-method that generates a key using the class name, +id+ and +updated_at+ timestamp (if available).
+In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+ method that generates a key using the class name, +id+ and +updated_at+ timestamp (if available).
You can access these cache stores at a low level for storing queries and other objects. Here's an example:
@@ -432,21 +316,11 @@ Rails.cache.read("city") # => "Duckburgh"
h3. Conditional GET support
-Conditional GETs are a feature of the HTTP specification that provide a way for web
-servers to tell browsers that the response to a GET request hasn't changed
-since the last request and can be safely pulled from the browser cache.
+Conditional GETs are a feature of the HTTP specification that provide a way for web servers to tell browsers that the response to a GET request hasn't changed since the last request and can be safely pulled from the browser cache.
-They work by using the +HTTP_IF_NONE_MATCH+ and +HTTP_IF_MODIFIED_SINCE+ headers
-to pass back and forth both a unique content identifier and the timestamp of
-when the content was last changed. If the browser makes a request where the
-content identifier (etag) or last modified since timestamp matches the server’s
-version then the server only needs to send back an empty response with a not
-modified status.
+They work by using the +HTTP_IF_NONE_MATCH+ and +HTTP_IF_MODIFIED_SINCE+ headers to pass back and forth both a unique content identifier and the timestamp of when the content was last changed. If the browser makes a request where the content identifier (etag) or last modified since timestamp matches the server’s version then the server only needs to send back an empty response with a not modified status.
-It is the server's (i.e. our) responsibility to look for a last modified
-timestamp and the if-none-match header and determine whether or not to send
-back the full response. With conditional-get support in rails this is a pretty
-easy task:
+It is the server's (i.e. our) responsibility to look for a last modified timestamp and the if-none-match header and determine whether or not to send back the full response. With conditional-get support in Rails this is a pretty easy task:
<ruby>
class ProductsController < ApplicationController
@@ -469,9 +343,7 @@ class ProductsController < ApplicationController
end
</ruby>
-If you don't have any special response processing and are using the default
-rendering mechanism (i.e. you're not using respond_to or calling render
-yourself) then you’ve got an easy helper in fresh_when:
+If you don't have any special response processing and are using the default rendering mechanism (i.e. you're not using respond_to or calling render yourself) then you’ve got an easy helper in fresh_when:
<ruby>
class ProductsController < ApplicationController
@@ -481,18 +353,14 @@ class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
- fresh_when :last_modified => @product.published_at.utc, :etag => @article
+ fresh_when :last_modified => @product.published_at.utc, :etag => @product
end
end
</ruby>
h3. Advanced Caching
-Along with the built-in mechanisms outlined above, a number of excellent
-plugins exist to help with finer grained control over caching. These include
-Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's
-interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both
-of these plugins play nice with memcached and are a must-see for anyone
+Along with the built-in mechanisms outlined above, a number of excellent plugins exist to help with finer grained control over caching. These include Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both of these plugins play nice with memcached and are a must-see for anyone
seriously considering optimizing their caching needs.
Also the new "Cache money":http://github.com/nkallen/cache-money/tree/master plugin is supposed to be mad cool.
@@ -501,7 +369,7 @@ h3. References
* "Scaling Rails Screencasts":http://railslab.newrelic.com/scaling-rails
* "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/2/28/rails-caching-tutorial
-* "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2
+* "RailsEnvy, Rails Caching Tutorial, Part 2":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2
* "ActiveSupport::Cache documentation":http://api.rubyonrails.org/classes/ActiveSupport/Cache.html
* "Rails 2.1 integrated caching tutorial":http://thewebfellas.com/blog/2008/6/9/rails-2-1-now-with-better-integrated-caching
@@ -509,8 +377,10 @@ h3. References
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching
-April 1, 2009: Made a bunch of small fixes
-February 22, 2009: Beefed up the section on cache_stores
-December 27, 2008: Typo fixes
-November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
-September 15, 2008: Initial version by Aditya Chadha
+* May 02, 2009: Formatting cleanups
+* April 26, 2009: Clean up typos in submitted patch
+* April 1, 2009: Made a bunch of small fixes
+* February 22, 2009: Beefed up the section on cache_stores
+* December 27, 2008: Typo fixes
+* November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
+* September 15, 2008: Initial version by Aditya Chadha
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index d97ed56eaf..2711337d43 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -194,7 +194,7 @@ Active Model currently has a single configuration setting:
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.
+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.
NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down.
diff --git a/railties/guides/source/contribute.textile b/railties/guides/source/contribute.textile
index 650004bd09..5087c2f385 100644
--- a/railties/guides/source/contribute.textile
+++ b/railties/guides/source/contribute.textile
@@ -40,7 +40,7 @@ For each completed guide, the lead contributor will receive all of the following
* $200 from Caboose Rails Documentation Project.
* 1 year of GitHub Micro account worth $84.
-* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x $10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product.
+* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x 10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product.
h3. Rules
diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
index 84778ed9ee..a5912643c0 100644
--- a/railties/guides/source/contributing_to_rails.textile
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -161,7 +161,7 @@ h4. Fork the Rails Source Code
Fork Rails. You’re not going to put your patches right into the master branch, OK? This is where you need that copy of Rails that you cloned earlier. Think of a name for your new branch and run
<shell>
-git checkout -b my_new_branch
+git checkout -b my_new_branch
</shell>
It doesn’t really matter what name you use, because this branch will only exist on your local computer.
@@ -175,6 +175,15 @@ Now get busy and add your code to Rails (or edit the existing code). You’re on
* Include tests that fail without your code, and pass with it
* Update the documentation
+h4. Follow the Coding Conventions
+
+Rails follows a simple set of coding style conventions.
+
+* Two spaces, no tabs
+* Prefer +&&+/+||+ over +and+/+or+
+* +MyClass.my_method(my_arg)+ not +my_method( my_arg )+ or +my_method my_arg+
+* Follow the conventions you see used in the source already
+
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.
diff --git a/railties/guides/source/credits.erb.textile b/railties/guides/source/credits.erb.textile
index b09a931fd6..49e390908f 100644
--- a/railties/guides/source/credits.erb.textile
+++ b/railties/guides/source/credits.erb.textile
@@ -8,15 +8,15 @@ p. We'd like to thank the following people for their tireless contributions to t
<h3 class="section">Rails Documentation Team</h3>
<% author('Mike Gunderloy', 'mgunderloy') do %>
- Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails activism team":http://rubyonrails.org/activists . He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much.
+ Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com. He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much.
<% end %>
<% author('Pratik Naik', 'lifo') do %>
Pratik Naik is a Ruby on Rails consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails core team":http://rubyonrails.org/core. He maintains a blog at "has_many :bugs, :through => :rails":http://m.onkey.org and has an active "twitter account":http://twitter.com/lifo.
<% end %>
-<% author('Xavier Noria', 'fxn', 'fxn.jpg') do %>
- Xavier Noria has been around dynamic languages since 2000. He fell in love with Rails in 2005, and cofounded Rails-based software company <a href="http://www.aspgems.com">ASPgems</a> in mid-2006. Xavier is president of the <a href="http://www.srug.org/">Spanish Ruby Users Group</a> and has been involved in Rails in several ways. He enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Oh, he also "tweets":http://twitter.com/fxn!
+<% author('Xavier Noria', 'fxn', 'fxn.png') do %>
+ Xavier has been into Rails since 2005, he is currently a Rails consultant. Xavier is president of the <a href="http://www.srug.org/">Spanish Ruby Users Group</a> and has been involved in Rails in several ways. He enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Oh, he also "tweets":http://twitter.com/fxn!
<% end %>
<h3 class="section">Rails Guides Designers</h3>
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index c059fdabf8..9c0f22724e 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -401,7 +401,7 @@ And then ask again for the instance_variables:
true
</shell>
-Now +@posts+ is a included in the instance variables, because the line defining it was executed.
+Now +@posts+ is included in the instance variables, because the line defining it was executed.
TIP: You can also step into *irb* mode with the command +irb+ (of course!). This way an irb session will be started within the context you invoked it. But be warned: this is an experimental feature.
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index 22d24b0903..ac0769e219 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -211,9 +211,7 @@ h4. Binding a Form to an Object
While this is an increase in comfort it is far from perfect. If Person has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object, which is exactly what +form_for+ does.
-Assume we have a controller for dealing with articles:
-
-articles_controller.rb:
+Assume we have a controller for dealing with articles +app/controllers/articles_controller.rb+:
<ruby>
def new
@@ -221,9 +219,7 @@ def new
end
</ruby>
-The corresponding view using +form_for+ looks like this
-
-articles/new.html.erb:
+The corresponding view +app/views/articles/new.html.erb+ using +form_for+ looks like this:
<erb>
<% form_for :article, @article, :url => { :action => "create" }, :html => {:class => "nifty_form"} do |f| %>
@@ -278,7 +274,13 @@ The object yielded by +fields_for+ is a form builder like the one yielded by +fo
h4. Relying on Record Identification
-The Article model is directly available to users of the application, so -- following the best practices for developing with Rails -- you should declare it *a resource*.
+The Article model is directly available to users of the application, so -- following the best practices for developing with Rails -- you should declare it *a resource*:
+
+<ruby>
+map.resources :articles
+</ruby>
+
+TIP: Declaring a resource has a number of side-affects. See "Rails Routing From the Outside In":routing.html#restful-routing-the-rails-default for more information on setting up and using resources.
When dealing with RESTful resources, calls to +form_for+ can get significantly easier if you rely on *record identification*. In short, you can just pass the model instance and have Rails figure out model name and the rest:
@@ -316,7 +318,7 @@ will create a form that submits to the articles controller inside the admin name
form_for [:admin, :management, @article]
</ruby>
-For more information on Rails' routing system and the associated conventions, please see the "routing guide":./routing_outside_in.html.
+For more information on Rails' routing system and the associated conventions, please see the "routing guide":routing.html.
h4. How do forms with PUT or DELETE methods work?
@@ -464,7 +466,7 @@ To leverage time zone support in Rails, you have to ask your users what time zon
There is also +time_zone_options_for_select+ helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.
-Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from rails).
+Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails).
h3. Using Date and Time Form Helpers
@@ -584,7 +586,7 @@ h4. Dealing with Ajax
Unlike other forms making an asynchronous file upload form is not as simple as replacing +form_for+ with +remote_form_for+. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission.
-h3. Customising Form Builders
+h3. Customizing Form Builders
As mentioned previously the object yielded by +form_for+ and +fields_for+ is an instance of FormBuilder (or a subclass thereof). Form builders encapsulate the notion of displaying form elements for a single object. While you can of course write helpers for your forms in the usual way you can also subclass FormBuilder and add the helpers there. For example
@@ -718,7 +720,7 @@ This will result in a +params+ hash that looks like
{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}}
</ruby>
-Rails knows that all these inputs should be part of the person hash because you called +fields_for+ on the first form builder. By specifying an +:index+ option you're telling rails that instead of naming the inputs +person[address][city]+ it should insert that index surrounded by [] between the address and the city. If you pass an Active Record object as we did then Rails will call +to_param+ on it, which by default returns the database id. This is often useful as it is then easy to locate which Address record should be modified. You can pass numbers with some other significance, strings or even +nil+ (which will result in an array parameter being created).
+Rails knows that all these inputs should be part of the person hash because you called +fields_for+ on the first form builder. By specifying an +:index+ option you're telling Rails that instead of naming the inputs +person[address][city]+ it should insert that index surrounded by [] between the address and the city. If you pass an Active Record object as we did then Rails will call +to_param+ on it, which by default returns the database id. This is often useful as it is then easy to locate which Address record should be modified. You can pass numbers with some other significance, strings or even +nil+ (which will result in an array parameter being created).
To create more intricate nestings, you can specify the first part of the input name (+person[address]+ in the previous example) explicitly, for example
@@ -736,7 +738,7 @@ will create inputs like
As a general rule the final input name is the concatenation of the name given to +fields_for+/+form_for+, the index value and the name of the attribute. You can also pass an +:index+ option directly to helpers such as +text_field+, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls.
-As a shortcut you can append [] to the name and omit the +:index+ option. This is the same as specifing +:index => address+ so
+As a shortcut you can append [] to the name and omit the +:index+ option. This is the same as specifying +:index => address+ so
<erb>
<% fields_for 'person[address][primary][]', address do |address_form| %>
@@ -750,7 +752,7 @@ h3. Building Complex Forms
Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include:
-* Ryan Bates' series of railscasts on "complex forms":http://railscasts.com/episodes/75
+* 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
* 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
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 7c029762a3..5c05648f12 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -9,7 +9,7 @@ This guide covers getting up and running with Ruby on Rails. After reading it, y
endprologue.
-WARNING. This Guide is based on Rails 2.3. Some of the code shown here will not work in older versions of Rails.
+WARNING. This Guide is based on Rails 2.3.3. Some of the code shown here will not work in other versions of Rails.
h3. This Guide Assumes
@@ -19,22 +19,22 @@ This guide is designed for beginners who want to get started with a Rails applic
* The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system
* A working installation of "SQLite":http://www.sqlite.org (preferred), "MySQL":http://www.mysql.com, or "PostgreSQL":http://www.postgresql.org
-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:
+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
+* "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/
+* "Why's (Poignant) Guide to Ruby":http://poignantguide.net/ruby/
h3. What is Rails?
-Rails is a web development framework written in the Ruby language. It is designed to make programming web applications easier by making several assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Longtime Rails developers also report that it makes web application development more fun.
+Rails is a web development framework written in the Ruby language. It is designed to make programming web applications easier by making assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Longtime Rails developers also report that it makes web application development more fun.
-Rails is opinionated software. That is, it assumes that there is a best way to do things, and it’s designed to encourage that best way - and in some cases to discourage alternatives. If you learn "The Rails Way" you’ll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience.
+Rails is opinionated software. That is, it assumes that there is a best way to do things, and it's designed to encourage that best way - and in some cases to discourage alternatives. If you learn "The Rails Way" you'll probably discover a tremendous increase in productivity. If you persist in bringing old habits from other languages to your Rails development, and trying to use patterns you learned elsewhere, you may have a less happy experience.
The Rails philosophy includes several guiding principles:
-* DRY - "Don’t Repeat Yourself" - suggests that writing the same code over and over again is a bad thing.
-* Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you’re going to do it, rather than letting you tweak every little thing through endless configuration files.
+* DRY - "Don't Repeat Yourself" - suggests that writing the same code over and over again is a bad thing.
+* Convention Over Configuration - means that Rails makes assumptions about what you want to do and how you're going to do it, rather than letting you tweak every little thing through endless configuration files.
* REST is the best pattern for web applications - organizing your application around resources and standard HTTP verbs is the fastest way to go.
h4. The MVC Architecture
@@ -47,7 +47,7 @@ Rails is organized around the Model, View, Controller architecture, usually just
h5. Models
-A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application’s business logic will be concentrated in the models.
+A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application's business logic will be concentrated in the models.
h5. Views
@@ -87,7 +87,7 @@ Action Mailer is a framework for building e-mail services. You can use Action Ma
h5. Active Resource
-Active Resource provides a framework for managing the connection between business objects an RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.
+Active Resource provides a framework for managing the connection between business objects and RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.
h5. Railties
@@ -99,7 +99,7 @@ Active Support is an extensive collection of utility classes and standard Ruby l
h4. REST
-The foundation of the RESTful architecture is generally considered to be Roy Fielding’s doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:
+The foundation of the RESTful architecture is generally considered to be Roy Fielding's doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:
* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
* Transferring representations of the state of that resource between system components.
@@ -110,7 +110,7 @@ For example, to a Rails application a request such as this:
would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities and browser quirks.
-If you’d like more details on REST as an architectural style, these resources are more approachable than Fielding’s thesis:
+If you'd like more details on REST as an architectural style, these resources are more approachable than Fielding's thesis:
* "A Brief Introduction to REST":http://www.infoq.com/articles/rest-introduction by Stefan Tilkov
* "An Introduction to REST":http://bitworking.org/news/373/An-Introduction-to-REST (video tutorial) by Joe Gregorio
@@ -119,7 +119,7 @@ If you’d like more details on REST as an architectural style, these resources
h3. Creating a New Rails Project
-If you follow this guide, you’ll create a Rails project called <tt>blog</tt>, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed.
+If you follow this guide, you'll create a Rails project called <tt>blog</tt>, a (very) simple weblog. Before you can start building the application, you need to make sure that you have Rails itself installed.
h4. Installing Rails
@@ -131,8 +131,10 @@ $ gem install rails
NOTE. There are some special circumstances in which you might want to use an alternate installation strategy:
-* If you’re working on Windows, you may find it easier to install Instant Rails. Be aware, though, that "Instant Rails":http://instantrails.rubyforge.org/wiki/wiki.pl releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows.
-* If you want to keep up with cutting-edge changes to Rails, you’ll want to clone the "Rails source code":http://github.com/rails/rails/tree/master from github. This is not recommended as an option for beginners, though.
+* If you're working on Windows, you may find it easier to install Instant Rails. Be aware, though, that "Instant Rails":http://instantrails.rubyforge.org/wiki/wiki.pl releases tend to lag seriously behind the actual Rails version. Also, you will find that Rails development on Windows is overall less pleasant than on other operating systems. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows.
+* If you want to keep up with cutting-edge changes to Rails, you'll want to clone the "Rails source code":http://github.com/rails/rails/tree/master from github. This is not recommended as an option for beginners, though.
+
+WARNING. As of mid-2009, cloning the master branch will get you preliminary Rails 3.0 code. To follow along with this guide, you should clone the 2-3-stable branch instead.
h4. Creating the Blog Application
@@ -148,13 +150,13 @@ This will create a Rails application that uses a SQLite database for data storag
$ rails blog -d mysql
</shell>
-And if you’re using PostgreSQL for data storage, run this command:
+And if you're using PostgreSQL for data storage, run this command:
<shell>
$ rails blog -d postgresql
</shell>
-TIP. You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>.
+TIP: You can see all of the switches that the Rails application builder accepts by running <tt>rails -h</tt>.
After you create the blog application, switch to its folder to continue work directly in that application:
@@ -162,7 +164,7 @@ After you create the blog application, switch to its folder to continue work dir
$ cd blog
</shell>
-In any case, Rails will create a folder in your working directory called <tt>blog</tt>. Open up that folder and explore its contents. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here’s a basic rundown on the function of each folder that Rails creates in a new application by default:
+In any case, Rails will create a folder in your working directory called <tt>blog</tt>. Open up that folder and explore its contents. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here's a basic rundown on the function of each folder that Rails creates in a new application by default:
|_.File/Folder|_.Purpose|
|README|This is a brief instruction manual for your application. Use it to tell others what your application does, how to set it up, and so on.|
@@ -259,7 +261,7 @@ One of the traditional places to start with a new language is by getting some te
$ script/generate controller home index
</shell>
-TIP. If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+.
+TIP: If you're on Windows, or your Ruby is set up in some non-standard fashion, you may need to explicitly pass Rails +script+ commands to Ruby: +ruby script/generate controller home index+.
Rails will create several files for you, including +app/views/home/index.html.erb+. This is the template that will be used to display the results of the +index+ action (method) in the +home+ controller. Open this file in your text editor and edit it to contain a single line of code:
@@ -279,7 +281,7 @@ This will fire up an instance of the Mongrel web server by default (Rails can al
!images/rails_welcome.png(Welcome Aboard screenshot)!
-TIP. To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.
+TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.
The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. To view the page you just created, navigate to +http://localhost:3000/home/index+.
@@ -383,7 +385,7 @@ NOTE: Because you're working in the development environment by default, this com
h4. Adding a Link
-To hook the posts up to the home page you've already created, you can add a link to the home page. Open +/app/views/home/index.html.erb+ and modify it as follows:
+To hook the posts up to the home page you've already created, you can add a link to the home page. Open +app/views/home/index.html.erb+ and modify it as follows:
<code lang="ruby">
<h1>Hello, Rails!</h1>
@@ -400,7 +402,7 @@ Now you're ready to start working with posts. To do that, navigate to +http://lo
This is the result of Rails rendering the +index+ view of your posts. There aren't currently any posts in the database, but if you click the +New Post+ link you can create one. After that, you'll find that you can edit posts, look at their details, or destroy them. All of the logic and HTML to handle this was built by the single +script/generate scaffold+ command.
-TIP. In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server.
+TIP: In development mode (which is what you're working in by default), Rails reloads your application with every browser request, so there's no need to stop and restart the web server.
Congratulations, you're riding the rails! Now it's time to see how it all works.
@@ -461,7 +463,7 @@ The easiest place to start looking at functionality is with the code that lists
<ruby>
def index
- @posts = Post.find(:all)
+ @posts = Post.all
respond_to do |format|
format.html # index.html.erb
@@ -486,7 +488,7 @@ The +respond_to+ block handles both HTML and XML calls to this action. If you br
<th>Content</th>
</tr>
-<% for post in @posts %>
+<% @posts.each do |post| %>
<tr>
<td><%=h post.name %></td>
<td><%=h post.title %></td>
@@ -508,9 +510,9 @@ This view iterates over the contents of the +@posts+ array to display content an
* +h+ is a Rails helper method to sanitize displayed data, preventing cross-site scripting attacks
* +link_to+ builds a hyperlink to a particular destination
-* +edit_post_path+ is a helper that Rails provides as part of RESTful routing. You’ll see a variety of these helpers for the different actions that the controller includes.
+* +edit_post_path+ is a helper that Rails provides as part of RESTful routing. You'll see a variety of these helpers for the different actions that the controller includes.
-TIP. For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html.
+TIP: For more details on the rendering process, see "Layouts and Rendering in Rails":layouts_and_rendering.html.
h4. Customizing the Layout
@@ -582,7 +584,7 @@ The +new.html.erb+ view displays this empty Post to the user:
<%= link_to 'Back', posts_path %>
</erb>
-The +form_for+ block is used to create an HTML form. Within this block, you have access to methods to build various controls on the form. For example, +f.text_field :name+ tells Rails to create a text input on the form, and to hook it up to the +name+ attribute of the instance being displayed. You can only use these methods with attributes of the model that the form is based on (in this case +name+, +title+, and +content+). Rails uses +form_for+ in preference to having your write raw HTML because the code is more succinct, and because it explicitly ties the form to a particular model instance.
+The +form_for+ block is used to create an HTML form. Within this block, you have access to methods to build various controls on the form. For example, +f.text_field :name+ tells Rails to create a text input on the form, and to hook it up to the +name+ attribute of the instance being displayed. You can only use these methods with attributes of the model that the form is based on (in this case +name+, +title+, and +content+). Rails uses +form_for+ in preference to having you write raw HTML because the code is more succinct, and because it explicitly ties the form to a particular model instance.
TIP: If you need to create an HTML form that displays arbitrary fields, not tied to a model, you should use the +form_tag+ method, which provides shortcuts for building forms that are not necessarily tied to a model instance.
@@ -609,7 +611,7 @@ end
The +create+ action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the +params+ hash. After saving the new post, it uses +flash[:notice]+ to create an informational message for the user, and redirects to the show action for the post. If there's any problem, the +create+ action just shows the +new+ view a second time, with any error messages.
-Rails provides the +flash+ hash (usually just called the Flash) so that messages can be carried over to another action, providing the user with useful information on the status of their request. In the case of +create+, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record. The Flash carries over a message to the next action, so that when the user is redirected back to the +show+ action, they are presented with a message saying "Post was successfully created."
+Rails provides the +flash+ hash (usually just called the Flash) so that messages can be carried over to another action, providing the user with useful information on the status of their request. In the case of +create+, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record. The Flash carries over a message to the next action, so that when the user is redirected back to the +show+ action, they are presented with a message saying "Post was successfully created."
h4. Showing an Individual Post
@@ -710,7 +712,7 @@ end
In the +update+ action, Rails first uses the +:id+ parameter passed back from the edit view to locate the database record that's being edited. The +update_attributes+ call then takes the rest of the parameters from the request and applies them to this record. If all goes well, the user is redirected to the post's +show+ view. If there are any problems, it's back to +edit+ to correct them.
-NOTE. Sharp-eyed readers will have noticed that the +form_for+ declaration is identical for the +new+ and +edit+ views. Rails generates different code for the two forms because it's smart enough to notice that in the one case it's being passed a new record that has never been saved, and in the other case an existing record that has already been saved to the database. In a production Rails application, you would ordinarily eliminate this duplication by moving identical code to a _partial template_, which you could then include in both parent templates. But the scaffold generator tries not to make too many assumptions, and generates code that’s easy to modify if you want different forms for +create+ and +edit+.
+NOTE. Sharp-eyed readers will have noticed that the +form_for+ declaration is identical for the +new+ and +edit+ views. Rails generates different code for the two forms because it's smart enough to notice that in the one case it's being passed a new record that has never been saved, and in the other case an existing record that has already been saved to the database. In a production Rails application, you would ordinarily eliminate this duplication by moving identical code to a _partial template_, which you could then include in both parent templates. But the scaffold generator tries not to make too many assumptions, and generates code that's easy to modify if you want different forms for +create+ and +edit+.
h4. Destroying a Post
@@ -732,7 +734,7 @@ The +destroy+ method of an Active Record model instance removes the correspondin
h3. DRYing up the Code
-At this point, it’s worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views and _filters_ to help with duplication in controllers.
+At this point, it's worth looking at some of the tools that Rails provides to eliminate duplication in your code. In particular, you can use _partials_ to clean up duplication in views and _filters_ to help with duplication in controllers.
h4. Using Partials to Eliminate View Duplication
@@ -740,13 +742,13 @@ As you saw earlier, the scaffold-generated views for the +new+ and +edit+ action
<tt>new.html.erb</tt>:
-<html>
+<erb>
<h1>New post</h1>
<%= render :partial => "form" %>
<%= link_to 'Back', posts_path %>
-</html>
+</erb>
<tt>edit.html.erb</tt>:
@@ -785,11 +787,11 @@ As you saw earlier, the scaffold-generated views for the +new+ and +edit+ action
Now, when Rails renders the +new+ or +edit+ view, it will insert the +_form+ partial at the indicated point. Note the naming convention for partials: if you refer to a partial named +form+ inside of a view, the corresponding file is +_form.html.erb+, with a leading underscore.
-For more information on partials, refer to the "Layouts and Rending in Rails":layouts_and_rendering.html guide.
+For more information on partials, refer to the "Layouts and Rendering in Rails":layouts_and_rendering.html#using-partials guide.
h4. Using Filters to Eliminate Controller Duplication
-At this point, if you look at the controller for posts, you’ll see some duplication:
+At this point, if you look at the controller for posts, you'll see some duplication:
<ruby>
class PostsController < ApplicationController
@@ -815,7 +817,7 @@ class PostsController < ApplicationController
end
</ruby>
-Four instances of the exact same line of code doesn’t seem very DRY. Rails provides _filters_ as a way to address this sort of repeated code. In this case, you can DRY things up by using a +before_filter+:
+Four instances of the exact same line of code doesn't seem very DRY. Rails provides _filters_ as a way to address this sort of repeated code. In this case, you can DRY things up by using a +before_filter+:
<ruby>
class PostsController < ApplicationController
@@ -837,10 +839,10 @@ class PostsController < ApplicationController
# ...
end
- private
- def find_post
- @post = Post.find(params[:id])
- end
+private
+ def find_post
+ @post = Post.find(params[:id])
+ end
end
</ruby>
@@ -944,7 +946,7 @@ map.resources :posts, :has_many => :comments
This creates +comments+ as a _nested resource_ within +posts+. This is another part of capturing the hierarchical relationship that exists between posts and comments.
-TIP: For more information on routing, see the "Rails Routing from the Outside In":routing_outside_in.html guide.
+TIP: For more information on routing, see the "Rails Routing from the Outside In":routing.html guide.
h4. Generating a Controller
@@ -954,7 +956,7 @@ With the model in hand, you can turn your attention to creating a matching contr
$ script/generate controller Comments index show new edit
</shell>
-This creates seven files:
+This creates eight files:
* +app/controllers/comments_controller.rb+ - The controller
* +app/helpers/comments_helper.rb+ - A view helper file
@@ -963,6 +965,7 @@ This creates seven files:
* +app/views/comments/new.html.erb+ - The view for the new action
* +app/views/comments/edit.html.erb+ - The view for the edit action
* +test/functional/comments_controller_test.rb+ - The functional tests for the controller
+* +test/unit/helpers/comments_helper_test.rb+ - The unit tests for the helper
The controller will be generated with empty methods and views for each action that you specified in the call to +script/generate controller+:
@@ -987,23 +990,21 @@ You'll need to flesh this out with code to actually process requests appropriate
<ruby>
class CommentsController < ApplicationController
+ before_filter :find_post
+
def index
- @post = Post.find(params[:post_id])
@comments = @post.comments
end
def show
- @post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
end
def new
- @post = Post.find(params[:post_id])
@comment = @post.comments.build
end
def create
- @post = Post.find(params[:post_id])
@comment = @post.comments.build(params[:comment])
if @comment.save
redirect_to post_comment_url(@post, @comment)
@@ -1013,12 +1014,10 @@ class CommentsController < ApplicationController
end
def edit
- @post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
end
def update
- @post = Post.find(params[:post_id])
@comment = Comment.find(params[:id])
if @comment.update_attributes(params[:comment])
redirect_to post_comment_url(@post, @comment)
@@ -1028,14 +1027,14 @@ class CommentsController < ApplicationController
end
def destroy
- @post = Post.find(params[:post_id])
@comment = Comment.find(params[:id])
@comment.destroy
+ redirect_to post_comments_path(@post)
+ end
- respond_to do |format|
- format.html { redirect_to post_comments_path(@post) }
- format.xml { head :ok }
- end
+private
+ def find_post
+ @post = Post.find(params[:post_id])
end
end
@@ -1198,6 +1197,18 @@ As a next step, I'll modify the +views/posts/show.html.erb+ view to show the com
Note that each post has its own individual comments collection, accessible as +@post.comments+. That's a consequence of the declarative associations in the models. Path helpers such as +post_comments_path+ come from the nested route declaration in +config/routes.rb+.
+h4. Deleting Associated Objects
+
+If you decide at some point to delete a post, you likely want to delete the comments associated with that post as well. You can do so by taking advantage of the association option +dependent+. All you need to do is modify the +post.rb+ as follows:
+
+<ruby>
+class Post < ActiveRecord::Base
+ validates_presence_of :name, :title
+ validates_length_of :title, :minimum => 5
+ has_many :comments, :dependent => :destroy
+end
+</ruby>
+
h3. Building a Multi-Model Form
Comments and posts are edited on two separate forms - which makes sense, given the flow of this mini-application. But what if you want to edit more than one thing on a single form? Rails 2.3 offers new support for nested forms. Let's add support for giving each post multiple tags, right in the form where you create the post. First, create a new model to hold the tags:
@@ -1212,7 +1223,7 @@ Run the migration to create the database table:
$ rake db:migrate
</shell>
-Next, edit the +post.rb+ file to create the other side of the association, and to tell Rails that you intend to edit tags via posts:
+Next, edit the +post.rb+ file to create the other side of the association, and to tell Rails (via the +accepts_nested_attributes+ macro) that you intend to edit tags via posts:
<ruby>
class Post < ActiveRecord::Base
@@ -1221,7 +1232,7 @@ class Post < ActiveRecord::Base
has_many :comments
has_many :tags
- accepts_nested_attributes_for :tags, :allow_destroy => :true ,
+ accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end
</ruby>
@@ -1240,7 +1251,7 @@ You'll also need to modify +views/posts/_form.html.erb+ to include the tags:
<%= post_form.text_field :name %>
</p>
<p>
- <%= post_form.label :title, "title" %><br />
+ <%= post_form.label :title, "Title" %><br />
<%= post_form.text_field :title %>
</p>
<p>
@@ -1277,17 +1288,19 @@ Now that you've seen your first Rails application, you should feel free to updat
* The "Ruby On Rails guides":http://guides.rubyonrails.org
* The "Ruby on Rails mailing list":http://groups.google.com/group/rubyonrails-talk
-* The #rubyonrails channel on irc.freenode.net
+* The "#rubyonrails":irc://irc.freenode.net/#rubyonrails channel on irc.freenode.net
+* The "Rails Wiki":http://wiki.rubyonrails.org/
Rails also comes with built-in help that you can generate using the rake command-line utility:
-* Running +rake doc:guides+ will put a full copy of the Rails Guides in the +/doc/guides+ folder of your application. Open +/doc/guides/index.html+ in your web browser to explore the Guides.
-* Running +rake doc:rails+ will put a full copy of the API documentation for Rails in the +/doc/api+ folder of your application. Open +/doc/api/index.html+ in your web browser to explore the API documentation.
+* Running +rake doc:guides+ will put a full copy of the Rails Guides in the +doc/guides+ folder of your application. Open +doc/guides/index.html+ in your web browser to explore the Guides.
+* Running +rake doc:rails+ will put a full copy of the API documentation for Rails in the +doc/api+ folder of your application. Open +doc/api/index.html+ in your web browser to explore the API documentation.
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2
+* July 18, 2009: Minor cleanup in anticipation of Rails 2.3.3 by "Mike Gunderloy":credits.html#mgunderloy
* February 1, 2009: Updated for Rails 2.3 by "Mike Gunderloy":credits.html#mgunderloy
* November 3, 2008: Formatting patch from Dave Rothlisberger
* November 1, 2008: First approved version by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index 103ccb1c7a..b588656821 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -97,7 +97,7 @@ The *translations load path* (+I18n.load_path+) is just a Ruby Array of paths to
NOTE: The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced.
-The default +environment.rb+ files has instruction how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
+The default +environment.rb+ files has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
<ruby>
# The internationalization framework can be changed
@@ -114,7 +114,7 @@ For the sake of completeness, let's mention that if you do not want to use the +
To tell the I18n library where it can find your custom translation files you can specify the load path anywhere in your application - just make sure it gets run before any translations are actually looked up. You might also want to change the default locale. The simplest thing possible is to put the following into an initializer:
<ruby>
-# in config/initializer/locale.rb
+# in config/initializers/locale.rb
# tell the I18n library where to find your translations
I18n.load_path << Dir[ File.join(RAILS_ROOT, 'lib', 'locale',
@@ -142,61 +142,28 @@ def set_locale
end
</ruby>
-This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is, for example, Google's approach.) So +http://localhost:3000?locale=pt+ will load the Portugese localization, whereas +http://localhost:3000?locale=de+ would load the German localization, and so on. You may skip the next section and head over to the *Internationalize your application* section, if you want to try things out by manually placing the locale in the URL and reloading the page.
+This requires you to pass the locale as a URL query parameter as in +http://example.com/books?locale=pt+. (This is, for example, Google's approach.) So +http://localhost:3000?locale=pt+ will load the Portuguese localization, whereas +http://localhost:3000?locale=de+ would load the German localization, and so on. You may skip the next section and head over to the *Internationalize your application* section, if you want to try things out by manually placing the locale in the URL and reloading the page.
Of course, you probably don't want to manually include the locale in every URL all over your application, or want the URLs look differently, e.g. the usual +http://example.com/pt/books+ versus +http://example.com/en/books+. Let's discuss the different options you have.
-IMPORTANT: The following examples rely on having available locales loaded into your application as an array of strings like +["en", "es", "gr"]+. This is not included in the current version of Rails 2.2 -- the forthcoming Rails version 2.3 will contain the easy accessor +available_locales+. (See "this commit":http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4 and background at "Rails I18n Wiki":http://rails-i18n.org/wiki/pages/i18n-available_locales.)
-
-So, for having available locales easily accessible in Rails 2.2, we have to include this support manually in an initializer, like this:
-
-<ruby>
-# config/initializers/available_locales.rb
-#
-# Get loaded locales conveniently
-# See http://rails-i18n.org/wiki/pages/i18n-available_locales
-module I18n
- class << self
- def available_locales; backend.available_locales; end
- end
- module Backend
- class Simple
- def available_locales; translations.keys.collect { |l| l.to_s }.sort; end
- end
- end
-end
-
-# You need to "force-initialize" loaded locales
-I18n.backend.send(:init_translations)
-
-AVAILABLE_LOCALES = I18n.backend.available_locales
-RAILS_DEFAULT_LOGGER.debug "* Loaded locales: #{AVAILABLE_LOCALES.inspect}"
-</ruby>
-
-You can then wrap the constant for easy access in ApplicationController:
-
-<ruby>
-class ApplicationController < ActionController::Base
- def available_locales; AVAILABLE_LOCALES; end
-end
-</ruby>
-
h4. Setting the Locale from the Domain Name
One option you have is to set the locale from the domain name where your application runs. For example, we want +www.example.com+ to load the English (or default) locale, and +www.example.es+ to load the Spanish locale. Thus the _top-level domain name_ is used for locale setting. This has several advantages:
-* The locale is an _obvious_ part of the URL
-* People intuitively grasp in which language the content will be displayed
-* It is very trivial to implement in Rails
-* Search engines seem to like that content in different languages lives at different, inter-linked domains
+* The locale is an _obvious_ part of the URL.
+* People intuitively grasp in which language the content will be displayed.
+* It is very trivial to implement in Rails.
+* Search engines seem to like that content in different languages lives at different, inter-linked domains.
-You can implement it like this in your ApplicationController:
+You can implement it like this in your +ApplicationController+:
<ruby>
before_filter :set_locale
+
def set_locale
I18n.locale = extract_locale_from_uri
end
+
# Get locale from top-level domain or return nil if such locale is not available
# You have to put something like:
# 127.0.0.1 application.com
@@ -205,7 +172,7 @@ end
# in your /etc/hosts file to try this out locally
def extract_locale_from_tld
parsed_locale = request.host.split('.').last
- (available_locales.include? parsed_locale) ? parsed_locale : nil
+ I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
end
</ruby>
@@ -218,7 +185,7 @@ We can also set the locale from the _subdomain_ in a very similar way:
# in your /etc/hosts file to try this out locally
def extract_locale_from_subdomain
parsed_locale = request.subdomains.first
- (available_locales.include? parsed_locale) ? parsed_locale : nil
+ I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
end
</ruby>
@@ -276,11 +243,11 @@ 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 explicitely, 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/pages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki.
h4. Setting the Locale from the Client Supplied Information
-In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' prefered language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites -- see the box about _sessions_, _cookies_ and RESTful architecture above.
+In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' preferred language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites -- see the box about _sessions_, _cookies_ and RESTful architecture above.
h5. Using +Accept-Language+
@@ -305,7 +272,7 @@ Of course, in a production environment you would need much more robust code, and
h5. Using GeoIP (or Similar) Database
-Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query the database for the user's IP, and look up your prefered locale for the country/region/city returned.
+Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as "GeoIP Lite Country":http://www.maxmind.com/app/geolitecountry. The mechanics of the code would be very similar to the code above -- you would need to query the database for the user's IP, and look up your preferred locale for the country/region/city returned.
h5. User Profile
@@ -315,7 +282,7 @@ h3. Internationalizing your Application
OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. With that in place, you're now ready for the really interesting stuff.
-Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide neccessary translations for these abstracts.
+Let's _internationalize_ our application, i.e. abstract every locale-specific parts, and then _localize_ it, i.e. provide necessary translations for these abstracts.
You most probably have something like this in one of your applications:
@@ -365,12 +332,12 @@ NOTE: Rails adds a +t+ (+translate+) helper method to your views so that you do
So let's add the missing translations into the dictionary files (i.e. do the "localization" part):
<ruby>
-# config/locale/en.yml
+# config/locales/en.yml
en:
hello_world: Hello World
hello_flash: Hello Flash
-# config/locale/pirate.yml
+# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash
@@ -378,7 +345,7 @@ pirate:
There you go. Because you haven't changed the default_locale, I18n will use English. Your application now shows:
-!images/i18n/demo_translated_en.png(rails i18n demo translated to english)!
+!images/i18n/demo_translated_en.png(rails i18n demo translated to English)!
And when you change the URL to pass the pirate locale (+http://localhost:3000?locale=pirate+), you'll get:
@@ -386,7 +353,7 @@ And when you change the URL to pass the pirate locale (+http://localhost:3000?lo
NOTE: You need to restart the server when you add new locale files.
-You may use YAML (+.yml+) or plain Ruby (+.rb+) files for storing your translations in SimpleStore. YAML is the prefered option among Rails developers. However, it has one big disadvantage. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.)
+You may use YAML (+.yml+) or plain Ruby (+.rb+) files for storing your translations in SimpleStore. YAML is the preferred option among Rails developers. However, it has one big disadvantage. YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly. Ruby files will crash your application on first request, so you may easily find what's wrong. (If you encounter any "weird issues" with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.)
h4. Adding Date/Time Formats
@@ -402,7 +369,7 @@ OK! Now let's add a timestamp to the view, so we can demo the *date/time localiz
And in our pirate translations file let's add a time format (it's already there in Rails' defaults for English):
<ruby>
-# config/locale/pirate.yml
+# config/locales/pirate.yml
pirate:
time:
formats:
@@ -413,7 +380,7 @@ So that would give you:
!images/i18n/demo_localized_pirate.png(rails i18n demo localized time to pirate)!
-TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails's defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locale/+ directory, they will automatically be ready for use.
+TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by *translating Rails' defaults for your locale*. See the "rails-i18n repository at Github":http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale for an archive of various locale files. When you put such file(s) in +config/locales/+ directory, they will automatically be ready for use.
h4. Localized Views
@@ -425,7 +392,7 @@ h4. Organization of Locale Files
When you are using the default SimpleStore shipped with the i18n library, dictionaries are stored in plain-text files on the disc. Putting translations for all parts of your application in one file per locale could be hard to manage. You can store these files in a hierarchy which makes sense to you.
-For example, your +config/locale+ directory could look like this:
+For example, your +config/locales+ directory could look like this:
<pre>
|-defaults
@@ -463,7 +430,7 @@ Do check the "Rails i18n Wiki":http://rails-i18n.org/wiki for list of tools avai
h3. Overview of the I18n API Features
-You should have good understanding of using the i18n library now, knowing all neccessary aspects of internationalizing a basic Rails application. In the following chapters, we'll cover it's features in more depth.
+You should have good understanding of using the i18n library now, knowing all necessary aspects of internationalizing a basic Rails application. In the following chapters, we'll cover it's features in more depth.
Covered are features like these:
@@ -686,7 +653,7 @@ en:
# will translate User attribute "login" as "Handle"
</ruby>
-Then +User.human_name+ will return "Dude" and +User.human_attribute_name(:login)+ will return "Handle".
+Then +User.human_name+ will return "Dude" and +User.human_attribute_name("login")+ will return "Handle".
h5. Error Message Scopes
diff --git a/railties/guides/source/index.erb.textile b/railties/guides/source/index.erb.textile
index 4c8dd65a04..851d91da2f 100644
--- a/railties/guides/source/index.erb.textile
+++ b/railties/guides/source/index.erb.textile
@@ -1,5 +1,5 @@
<% content_for :header_section do %>
-h2. Ruby on Rails guides
+h2. Ruby on Rails Guides
These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together. There are two different versions of the Guides site, and you should be sure to use the one that applies to your situation:
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 809d2b2172..0cee413ac3 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -204,7 +204,7 @@ TIP: You don't need to call +to_xml+ on the object that you want to render. If y
h5. Rendering Vanilla JavaScript
-Rails can render vanilla JavaScript (as an alternative to using +update+ with n +.rjs+ file):
+Rails can render vanilla JavaScript (as an alternative to using +update+ with an +.rjs+ file):
<ruby>
render :js => "alert('Hello Rails');"
@@ -266,7 +266,7 @@ render :xml => photo, :location => photo_url(photo)
h5. Finding Layouts
-To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +/app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +/app/views/layouts/application.html.erb+ ot +/app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.
+To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +app/views/layouts/application.html.erb+ or +app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.
h6. Specifying Layouts on a per-Controller Basis
@@ -418,15 +418,15 @@ def show
end
</ruby>
-Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. So this code will work with problems:
+Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. Therefore, the following will work without errors:
<ruby>
- def show
- @book = Book.find(params[:id])
- if @book.special?
- render :action => "special_show"
- end
+def show
+ @book = Book.find(params[:id])
+ if @book.special?
+ render :action => "special_show"
end
+end
</ruby>
This will render a book with +special?+ set with the +special_show+ template, while other books will render with the default +show+ template.
@@ -447,7 +447,7 @@ redirect_to :back
h5. Getting a Different Redirect Status Code
-Rails uses HTTP status code 302 (permanent redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, temporary redirect), you can do so by using the +:status+ option:
+Rails uses HTTP status code 302 (temporary redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, permanent redirect), you can do so by using the +:status+ option:
<ruby>
redirect_to photos_path, :status => 301
@@ -519,10 +519,10 @@ h4. Asset Tags
Asset tags provide methods for generating HTML that links views to assets like images, javascript, stylesheets, and feeds. There are four types of include tag:
-* auto_discovery_link_tag
-* javascript_include_tag
-* stylesheet_link_tag
-* image_tag
+* +auto_discovery_link_tag+
+* +javascript_include_tag+
+* +stylesheet_link_tag+
+* +image_tag+
You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +&lt;head&gt;+ section of a layout.
@@ -622,16 +622,16 @@ To include +public/stylesheets/main.css+ and +public/photos/columns.css+:
<%= stylesheet_link_tag "main", "/photos/columns" %>
</erb>
-To include +http://example.com/main.cs+:
+To include +http://example.com/main.css+:
<erb>
-<%= stylesheet_link_tag "http://example.com/main.cs" %>
+<%= stylesheet_link_tag "http://example.com/main.css" %>
</erb>
-By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (:media, :rel, or :type):
+By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (+:media+, +:rel+, or +:type+):
<erb>
-<%= stylesheet_link_tag "main_print", media => "print" %>
+<%= stylesheet_link_tag "main_print", :media => "print" %>
</erb>
The +all+ option links every CSS file in +public/stylesheets+:
@@ -958,13 +958,13 @@ On pages generated by +NewsController+, you want to hide the top menu and add a
<% content_for :content do %>
<div id="right_menu">Right menu items here</div>
<%= yield(:news_content) or yield %>
- <% end -%>
+<% end -%>
<%= render :file => 'layouts/application' %>
</erb>
That's it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the "content" div.
-There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If one is sure she will not subtemplate the +News+ layout, she can ommit the +yield(:news_content) or + part.
+There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If one is sure she will not subtemplate the +News+ layout, she can omit the +yield(:news_content) or + part.
h3. Changelog
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile
index 5ed94c30b7..5f49ac41f5 100644
--- a/railties/guides/source/migrations.textile
+++ b/railties/guides/source/migrations.textile
@@ -1,10 +1,10 @@
h2. Migrations
-Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy.
+Migrations are a convenient way for you to alter your database in a structured and organized manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy.
Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database.
-Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more that you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
+Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more than you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
You'll learn all about migrations including:
@@ -78,7 +78,7 @@ Active Record provides methods that perform common data definition tasks in a da
If you need to perform tasks specific to your database (for example create a "foreign key":#active-record-and-referential-integrity constraint) then the +execute+ function allows you to execute arbitrary SQL. A migration is just a regular Ruby class so you're not limited to these functions. For example after adding a column you could write code to set the value of that column for existing records (if necessary using your models).
-On databases that support transactions with statements that change the schema (such as PostgreSQL), migrations are wrapped in a transaction. If the database does not support this (for example MySQL and SQLite) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.
+On databases that support transactions with statements that change the schema (such as PostgreSQL or SQLite3), migrations are wrapped in a transaction. If the database does not support this (for example MySQL) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.
h4. What's in a Name
@@ -508,7 +508,7 @@ The migration has its own minimal copy of the +Product+ model and no longer care
h4. Dealing with Changing Models
-For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try and use the old column information. You can force Active Record to re-read the column information with the +reset_column_information+ method, for example
+For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try to use the old column information. You can force Active Record to re-read the column information with the +reset_column_information+ method, for example
<ruby>
class AddPartNumberToProducts < ActiveRecord::Migration
@@ -538,7 +538,7 @@ There is no need (and it is error prone) to deploy a new instance of an app by r
For example, this is how the test database is created: the current development database is dumped (either to +db/schema.rb+ or +db/development.sql+) and then loaded into the test database.
-Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest.
+Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarizing the schema, may also be of interest.
h4. Types of Schema Dumps
@@ -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 "redhillonrails":http://agilewebdevelopment.com/plugins/search?search=redhillonrails 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/foreign_key_migrations/tree/master 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/performance_testing.textile b/railties/guides/source/performance_testing.textile
index 320a5b8472..f0dc9acbb8 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -293,7 +293,7 @@ WARNING: Performance test configurability is not yet enabled in Rails. But it wi
h4. Performance Test Environment
-Performance tests are run in the +development+ environment. But running performance tests will set the following configuration parameters:
+Performance tests are run in the +test+ environment. But running performance tests will set the following configuration parameters:
<shell>
ActionController::Base.perform_caching = true
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index 55ecdcd3d1..a5d39c3d09 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -601,7 +601,7 @@ This is just a simple test to make sure the class is being loaded correctly. Af
end
</ruby>
-Adding directories to the load path makes them appear just like files in the the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser. Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server. This is particularly useful as you develop the plugin.
+Adding directories to the load path makes them appear just like files in the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser. Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server. This is particularly useful as you develop the plugin.
* *vendor/plugins/yaffle/lib/app/models/woodpecker.rb:*
diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile
index 49cd5bf5f5..fc178fa44b 100644
--- a/railties/guides/source/rails_application_templates.textile
+++ b/railties/guides/source/rails_application_templates.textile
@@ -1,18 +1,238 @@
h2. Rails Application Templates
-This guide covers the Rails application templates, By referring to this guide, you will be able to:
+Application templates are simple ruby files containing DSL for adding plugins/gems/initializers etc. to your freshly created Rails project or an existing Rails project.
+
+By referring to this guide, you will be able to:
-* Use existing templates to generate a customized Rails application
-* Write your own reusable Rails application templates
+* Use templates to generate/customize Rails applications
+* Write your own reusable application templates using the Rails template API
endprologue.
-h3. Introduction
+h3. Usage
-Application templates are simple ruby files containing DSL for adding plugins/gems/initializers etc. to your freshly created Rails project or an existing Rails project.
+To apply a template, you need to provide the Rails generator with the location of the template you wish to apply, using -m option :
+
+<shell>
+$ rails blog -m ~/template.rb
+</shell>
+
+It's also possible to apply a template using a URL :
+
+<shell>
+$ rails blog -m http://gist.github.com/31208.txt
+</shell>
+
+Alternatively, you can use the rake task +rails:template+ to apply a template to an existing Rails application :
+
+<shell>
+$ rake rails:template LOCATION=~/template.rb
+</shell>
+
+h3. Template API
+
+Rails templates API is very self explanatory and easy to understand. Here's an example of a typical Rails template :
+
+<ruby>
+# template.rb
+run "rm public/index.html"
+generate(:scaffold, "person name:string")
+route "map.root :controller => 'people'"
+rake("db:migrate")
+
+git :init
+git :add => "."
+git :commit => "-a -m 'Initial commit'"
+</ruby>
+
+The following sections outlines the primary methods provided by the API :
+
+h4. gem(name, options = {})
+
+Adds a +config.gem+ entry for the supplied gem to the generated application’s +config/environment.rb+.
+
+For example, if your application depends on the gems +bj+ and +hpricot+ :
+
+<ruby>
+gem "bj"
+gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
+</ruby>
+
+Please note that this will NOT install the gems for you. So you may want to run the +rake gems:install+ task too :
+
+<ruby>
+rake "gems:install"
+</ruby>
+
+And let Rails take care of installing the required gems if they’re not already installed.
+
+h4. plugin(name, options = {})
+
+Installs a plugin to the generated application.
+
+Plugin can be installed from Git :
+
+<ruby>
+plugin 'authentication', :git => 'git://github.com/foor/bar.git'
+</ruby>
+
+You can even install plugins as git submodules :
+
+<ruby>
+plugin 'authentication', :git => 'git://github.com/foor/bar.git',
+ :submodule => true
+</ruby>
+
+Please note that you need to +git :init+ before you can install a plugin as a submodule.
+
+Or use plain old SVN :
+
+<ruby>
+plugin 'wtfsvn', :svn => 'svn://crap.com/wtf/trunk'
+</ruby>
+
+h4. vendor/lib/file/initializer(filename, data = nil, &block)
+
+Adds an initializer to the generated application’s +config/initializers+ directory.
+
+Lets say you like using +Object#not_nil?+ and +Object#not_blank?+ :
+
+<ruby>
+initializer 'bloatlol.rb', <<-CODE
+class Object
+ def not_nil?
+ !nil?
+ end
+
+ def not_blank?
+ !blank?
+ end
+end
+CODE
+</ruby>
+
+Similarly +lib()+ creates a file in the +lib/+ directory and +vendor()+ creates a file in the +vendor/+ directory.
+
+There is even +file()+, which accepts a relative path from +RAILS_ROOT+ and creates all the directories/file needed :
+
+<ruby>
+file 'app/components/foo.rb', <<-CODE
+class Foo
+end
+CODE
+</ruby>
+
+That’ll create +app/components+ directory and put +foo.rb+ in there.
+
+h4. rakefile(filename, data = nil, &block)
+
+Creates a new rake file under +lib/tasks+ with the supplied tasks :
+
+<ruby>
+rakefile("bootstrap.rake") do
+ <<-TASK
+ namespace :boot do
+ task :strap do
+ puts "i like boots!"
+ end
+ end
+ TASK
+end
+</ruby>
+
+The above creates +lib/tasks/bootstrap.rake+ with a +boot:strap+ rake task.
+
+h4. generate(what, args)
+
+Runs the supplied rails generator with given arguments. For example, I love to scaffold some whenever I’m playing with Rails :
+
+<ruby>
+generate(:scaffold, "person", "name:string", "address:text", "age:number")
+</ruby>
+
+h4. run(command)
+
+Executes an arbitrary command. Just like the backticks. Let's say you want to remove the +public/index.html+ file :
+
+<ruby>
+run "rm public/index.html"
+</ruby>
+
+h4. rake(command, options = {})
+
+Runs the supplied rake tasks in the Rails application. Let's say you want to migrate the database :
+
+<ruby>
+rake "db:migrate"
+</ruby>
+
+You can also run rake tasks with a different Rails environment :
+
+<ruby>
+rake "db:migrate", :env => 'production'
+</ruby>
+
+Or even use sudo :
+
+<ruby>
+rake "gems:install", :sudo => true
+</ruby>
+
+h4. route(routing_code)
+
+This adds a routing entry to the +config/routes.rb+ file. In above steps, we generated a person scaffold and also removed +public/index.html+. Now to make +PeopleController#index+ as the default page for the application :
+
+<ruby>
+route "map.root :controller => :person"
+</ruby>
+
+h4. inside(dir)
+
+I have my edge rails lying at +~/commit-rails/rails+. So every time i have to manually symlink edge from my new app. But now :
+
+<ruby>
+inside('vendor') do
+ run "ln -s ~/commit-rails/rails rails"
+end
+</ruby>
+
+So +inside()+ runs the command from the given directory.
+
+h4. ask(question)
+
++ask()+ gives you a chance to get some feedback from the user and use it in your templates. Lets say you want your user to name the new shiny library you’re adding :
+
+<ruby>
+lib_name = ask("What do you want to call the shiny library ?")
+lib_name << ".rb" unless lib_name.index(".rb")
+
+lib lib_name, <<-CODE
+class Shiny
+end
+CODE
+</ruby>
+
+h4. yes?(question) or no?(question)
+
+These methods let you ask questions from templates and decide the flow based on the user’s answer. Lets say you want to freeze rails only if the user want to :
+
+<ruby>
+rake("rails:freeze:gems") if yes?("Freeze rails gems ?")
+no?(question) acts just the opposite.
+</ruby>
+
+h4. git(:must => "-a love")
+
+Rails templates let you run any git command :
+
+<ruby>
+git :init
+git :add => "."
+git :commit => "-a -m 'Initial commit'"
+</ruby>
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/78
-* April 17, 2009: Initial version by "Pratik":credits.html#lifo
+* April 29, 2009: Initial version by "Pratik":credits.html#lifo
diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile
index 1164ed821d..545aaa18e0 100644
--- a/railties/guides/source/rails_on_rack.textile
+++ b/railties/guides/source/rails_on_rack.textile
@@ -48,7 +48,7 @@ app = Rack::Builder.new {
}.to_app
</ruby>
-Middlewares used in the code above are primarily useful only in the development envrionment. The following table explains their usage:
+Middlewares used in the code above are primarily useful only in the development environment. The following table explains their usage:
|_.Middleware|_.Purpose|
|+Rails::Rack::LogTailer+|Appends log file output to console|
@@ -229,7 +229,7 @@ h3. Rails Metal Applications
Rails Metal applications are minimal Rack applications specially designed for integrating with a typical Rails application. As Rails Metal Applications skip all of the Action Controller stack, serving a request has no overhead from the Rails framework itself. This is especially useful for infrequent cases where the performance of the full stack Rails framework is an issue.
-Ryan Bates' "railscast on Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal.
+Ryan Bates' "Railscast on Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal.
h4. Generating a Metal Application
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index e9adb4b308..355f385d49 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -236,7 +236,7 @@ will recognize incoming URLs containing +photo+ but route the requests to the Im
|GET |/photos/new |Images |new |return an HTML form for creating a new image|
|POST |/photos |Images |create |create a new image|
|GET |/photos/1 |Images |show |display a specific image|
-|GET |/photos/1/edit |Images |edit |return an HTML form for editing a image|
+|GET |/photos/1/edit |Images |edit |return an HTML form for editing an image|
|PUT |/photos/1 |Images |update |update a specific image|
|DELETE |/photos/1 |Images |destroy |delete a specific image|
@@ -416,7 +416,7 @@ map.resources :magazines do |magazine|
end
</ruby>
-TIP: Further below you'll learn about a convenient shortcut for this construct:<br/>+map.resources :magazines, :has_many => :ads+.
+TIP: Further below you'll learn about a convenient shortcut for this construct:<br/>+map.resources :magazines, :has_many => :ads+
In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL:
@@ -614,7 +614,7 @@ To add a new route (one that creates a new resource), use the +:new+ option:
map.resources :photos, :new => { :upload => :post }
</ruby>
-This will enable Rails to recognize URLs such as +/photos/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create the +upload_photos_path+ and +upload_photos_url+ route helpers.
+This will enable Rails to recognize URLs such as +/photos/new/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create the +upload_new_photos_path+ and +upload_new_photos_url+ route helpers.
TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:<br/>+map.resources :photos, :new => { :new => :any }+<br/>This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use.
@@ -695,7 +695,7 @@ Regular routes need not use the +connect+ method. You can use any other name her
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
</ruby>
-This will do two things. First, requests to +/logout+ will be sent to the +destroy+ method of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code.
+This will do two things. First, requests to +/logout+ will be sent to the +destroy+ action of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code.
h4. Route Requirements
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index 1b64cc1be7..c26bea5519 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -392,7 +392,7 @@ params[:user] #=> {:name => “ow3ned”, :admin => true}
So if you create a new user using mass-assignment, it may be too easy to become an administrator.
-Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in rails 2.3. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example:
+Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in Rails 2.3. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example:
<ruby>
class Person < ActiveRecord::Base
@@ -497,7 +497,7 @@ Depending on your web application, there may be more ways to hijack the user's a
h4. CAPTCHAs
--- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not to ask a user to proof that he is human, but reveal that a robot is a robot._
+-- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not for a user to prove that he is human, but reveal that a robot is a robot._
But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is "reCAPTCHA":http://recaptcha.net/ which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. "ReCAPTCHA":http://ambethia.com/recaptcha/ is also a Rails plug-in with the same name as the API.
@@ -967,7 +967,7 @@ Transfer-Encoding: chunked
Content-Type: text/html
</plain>
-Under certain circumstances this would present the malicious HTML to the victim. However, this seems to work with Keep-Alive connections, only (and many browsers are using one-time connections). But you can't rely on this. _(highlight)In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks._
+Under certain circumstances this would present the malicious HTML to the victim. However, this only seems to work with Keep-Alive connections (and many browsers are using one-time connections). But you can't rely on this. _(highlight)In any case this is a serious bug, and you should update your Rails to version 2.0.5 or 2.1.2 to eliminate Header Injection (and thus response splitting) risks._
h3. Additional Resources
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index 12fc836edf..8318146ed3 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -211,7 +211,7 @@ $ rake db:migrate
$ rake db:test:load
</shell>
-Above +rake db:migrate+ runs any pending migrations on the _developemnt_ environment and updates +db/schema.rb+. +rake db:test:load+ recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run +db:test:prepare+ as it first checks for pending migrations and warns you appropriately.
+Above +rake db:migrate+ runs any pending migrations on the _development_ environment and updates +db/schema.rb+. +rake db:test:load+ recreates the test database from the current db/schema.rb. On subsequent attempts it is a good to first run +db:test:prepare+ as it first checks for pending migrations and warns you appropriately.
NOTE: +db:test:prepare+ will fail with an error if db/schema.rb doesn't exists.
@@ -372,7 +372,7 @@ NameError: undefined local variable or method `some_undefined_variable' for #<Po
Notice the 'E' in the output. It denotes a test with error.
-NOTE: The execution of each test method stops as soon as any error or a assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
+NOTE: The execution of each test method stops as soon as any error or an assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
h4. What to Include in Your Unit Tests
@@ -413,6 +413,8 @@ h4. Rails Specific Assertions
Rails adds some custom assertions of its own to the +test/unit+ framework:
+NOTE: +assert_valid(record)+ has been deprecated. Please use +assert(record.valid?)+ instead.
+
|_.Assertion |_.Purpose|
|+assert_valid(record)+ |Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.|
|+assert_difference(expressions, difference = 1, message = nil) {...}+ |Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.|
@@ -437,7 +439,7 @@ You should test for things such as:
* was the user redirected to the right page?
* was the user successfully authenticated?
* was the correct object stored in the response template?
-* was the appropriate message displayed to the user in the view
+* was the appropriate message displayed to the user in the view?
Now that we have used Rails scaffold generator for our +Post+ resource, it has already created the controller code and functional tests. You can take look at the file +posts_controller_test.rb+ in the +test/functional+ directory.
@@ -590,7 +592,7 @@ There are more assertions that are primarily used in testing views:
|_.Assertion |_.Purpose|
|+assert_select_email+ |Allows you to make assertions on the body of an e-mail. |
-|+assert_select_rjs+ |Allows you to make assertions on RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.|
+|+assert_select_rjs+ |Allows you to make assertions on an RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.|
|+assert_select_encoded+ |Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.|
|+css_select(selector)+ or +css_select(element, selector)+ |Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.|
@@ -699,7 +701,7 @@ class UserFlowsTest < ActionController::IntegrationTest
# User avs can browse site
avs.browses_site
- # User guest can browse site aswell
+ # User guest can browse site as well
guest.browses_site
# Continue with other assertions
@@ -730,7 +732,7 @@ end
h3. Rake Tasks for Running your Tests
-You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project.
+You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rails project.
|_.Tasks |_.Description|
|+rake test+ |Runs all unit, functional and integration tests. You can also simply run +rake+ as the _test_ target is the default.|
@@ -836,7 +838,7 @@ end
h3. Testing Routes
-Like everything else in you Rails application, it's recommended to test you routes. An example test for a route in the default +show+ action of +Posts+ controller above should look like:
+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
@@ -860,7 +862,7 @@ The goals of testing your +ActionMailer+ classes are to ensure that:
h5. From All Sides
-There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a knownvalue (a fixture -- yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time.
+There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a known value (a fixture -- yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time.
h4. Unit Testing
diff --git a/railties/lib/commands/destroy.rb b/railties/lib/commands/destroy.rb
index f4b81d6511..5013d30b83 100644
--- a/railties/lib/commands/destroy.rb
+++ b/railties/lib/commands/destroy.rb
@@ -1,6 +1,10 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators'))
require "#{RAILS_ROOT}/config/environment"
-require 'rails_generator'
-require 'rails_generator/scripts/destroy'
-ARGV.shift if ['--help', '-h'].include?(ARGV[0])
-Rails::Generator::Scripts::Destroy.new.run(ARGV)
+if ARGV.size == 0
+ Rails::Generators.help
+ exit
+end
+
+name = ARGV.shift
+Rails::Generators.invoke name, ARGV, :behavior => :revoke
diff --git a/railties/lib/commands/generate.rb b/railties/lib/commands/generate.rb
index 3d3db3d856..32cabcab10 100755
--- a/railties/lib/commands/generate.rb
+++ b/railties/lib/commands/generate.rb
@@ -1,6 +1,10 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators'))
require "#{RAILS_ROOT}/config/environment"
-require 'rails_generator'
-require 'rails_generator/scripts/generate'
-ARGV.shift if ['--help', '-h'].include?(ARGV[0])
-Rails::Generator::Scripts::Generate.new.run(ARGV)
+if ARGV.size == 0
+ Rails::Generators.help
+ exit
+end
+
+name = ARGV.shift
+Rails::Generators.invoke name, ARGV, :behavior => :invoke
diff --git a/railties/lib/commands/update.rb b/railties/lib/commands/update.rb
index 83ef833300..f3b3ad0775 100644
--- a/railties/lib/commands/update.rb
+++ b/railties/lib/commands/update.rb
@@ -1,4 +1,10 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators'))
require "#{RAILS_ROOT}/config/environment"
-require 'rails_generator'
-require 'rails_generator/scripts/update'
-Rails::Generator::Scripts::Update.new.run(ARGV)
+
+if ARGV.size == 0
+ Rails::Generators.help
+ exit
+end
+
+name = ARGV.shift
+Rails::Generators.invoke name, ARGV, :behavior => :skip
diff --git a/railties/lib/generators.rb b/railties/lib/generators.rb
new file mode 100644
index 0000000000..64ec808ee4
--- /dev/null
+++ b/railties/lib/generators.rb
@@ -0,0 +1,300 @@
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift(activesupport_path) if File.directory?(activesupport_path)
+
+begin
+ require 'active_support/mini'
+rescue LoadError
+ require 'rubygems'
+ gem 'activesupport'
+ require 'active_support/mini'
+end
+
+$:.unshift(File.dirname(__FILE__))
+
+require 'vendor/thor-0.11.3/lib/thor'
+require 'generators/base'
+require 'generators/named_base'
+
+module Rails
+ module Generators
+ DEFAULT_ALIASES = {
+ :rails => {
+ :actions => '-a',
+ :orm => '-o',
+ :resource_controller => '-c',
+ :scaffold_controller => '-c',
+ :stylesheets => '-y',
+ :template_engine => '-e',
+ :test_framework => '-t'
+ },
+
+ :test_unit => {
+ :fixture_replacement => '-r',
+ },
+
+ :plugin => {
+ :generator => '-g',
+ :tasks => '-r'
+ }
+ }
+
+ DEFAULT_OPTIONS = {
+ :active_record => {
+ :migration => true,
+ :timestamps => true
+ },
+
+ :erb => {
+ :form => false,
+ :layout => true
+ },
+
+ :rails => {
+ :force_plural => false,
+ :helper => true,
+ :layout => true,
+ :orm => :active_record,
+ :integration_tool => :test_unit,
+ :performance_tool => :test_unit,
+ :resource_controller => :controller,
+ :scaffold_controller => :scaffold_controller,
+ :singleton => false,
+ :stylesheets => true,
+ :template_engine => :erb,
+ :test_framework => :test_unit
+ },
+
+ :test_unit => {
+ :fixture => true,
+ :fixture_replacement => nil
+ },
+
+ :plugin => {
+ :generator => false,
+ :tasks => false
+ }
+ }
+
+ def self.aliases #:nodoc:
+ @@aliases ||= DEFAULT_ALIASES.dup
+ end
+
+ def self.options #:nodoc:
+ @@options ||= DEFAULT_OPTIONS.dup
+ 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.
+ #
+ # For example, shoulda is considered a test_framework and is an extension
+ # of test_unit. However, most part of shoulda generators are similar to
+ # test_unit ones.
+ #
+ # Shoulda then can tell generators to search for test_unit generators when
+ # some of them are not available by adding a fallback:
+ #
+ # Rails::Generators.fallbacks[:shoulda] = :test_unit
+ #
+ def self.fallbacks
+ @@fallbacks ||= {}
+ end
+
+ # Remove the color from output.
+ #
+ def self.no_color!
+ Thor::Base.shell = Thor::Shell::Basic
+ end
+
+ # Generators load paths used on lookup. The lookup happens as:
+ #
+ # 1) builtin generators
+ # 2) frozen gems generators
+ # 3) rubygems gems generators (not available yet)
+ # 4) plugin generators
+ # 5) lib generators
+ # 6) ~/rails/generators
+ #
+ # TODO Add Rubygems generators (depends on dependencies system rework)
+ # TODO Remove hardcoded paths for all, except (1).
+ #
+ def self.load_path
+ @@load_path ||= begin
+ paths = []
+ paths << File.expand_path(File.join(File.dirname(__FILE__), "generators"))
+ if defined?(RAILS_ROOT)
+ paths += Dir[File.join(RAILS_ROOT, "vendor", "gems", "*", "lib", "generators")]
+ paths += Dir[File.join(RAILS_ROOT, "vendor", "plugins", "*", "lib", "generators")]
+ paths << File.join(RAILS_ROOT, "lib", "generators")
+ end
+ paths << File.join(Thor::Util.user_home, ".rails", "generators")
+ paths
+ end
+ end
+ load_path # Cache load paths. Needed to avoid __FILE__ pointing to wrong paths.
+
+ # Receives a namespace and tries different combinations to find a generator.
+ #
+ # ==== Examples
+ #
+ # find_by_namespace :webrat, :rails, :integration
+ #
+ # Will search for the following generators:
+ #
+ # "rails:generators:webrat", "webrat:generators:integration", "webrat"
+ #
+ # If the namespace has ":" included we consider that a absolute namespace
+ # was given and the lookup above does not happen. Just the name is searched.
+ #
+ # Finally, it deals with one kind of shortcut:
+ #
+ # find_by_namespace "test_unit:model"
+ #
+ # It will search for generators at:
+ #
+ # "test_unit:generators:model", "test_unit:model"
+ #
+ def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
+ name, attempts = name.to_s, []
+
+ case name.count(':')
+ when 1
+ base, name = name.split(':')
+ return find_by_namespace(name, base)
+ when 0
+ attempts << "#{base}:generators:#{name}" if base
+ attempts << "#{name}:generators:#{context}" if context
+ end
+
+ attempts << name
+ unloaded = attempts - namespaces
+ lookup(unloaded)
+
+ attempts.each do |namespace|
+ klass = Thor::Util.find_by_namespace(namespace)
+ return klass if klass
+ end
+
+ invoke_fallbacks_for(name, base)
+ end
+
+ # Receives a namespace, arguments and the behavior to invoke the generator.
+ # It's used as the default entry point for generate, destroy and update
+ # commands.
+ #
+ def self.invoke(namespace, args=ARGV, config={})
+ if klass = find_by_namespace(namespace, "rails")
+ args << "--help" if klass.arguments.any? { |a| a.required? } && args.empty?
+ klass.start args, config
+ else
+ puts "Could not find generator #{namespace}."
+ end
+ end
+
+ # Show help message with available generators.
+ #
+ def self.help
+ rails = Rails::Generators.builtin.map do |group, name|
+ name if group == "rails"
+ end
+ rails.compact!
+ rails.sort!
+
+ puts "Please select a generator."
+ puts "Builtin: #{rails.join(', ')}."
+
+ # Load paths and remove builtin
+ paths, others = load_path.dup, []
+ paths.shift
+
+ paths.each do |path|
+ tail = [ "*", "*", "*_generator.rb" ]
+
+ until tail.empty?
+ others += Dir[File.join(path, *tail)].collect do |file|
+ file.split('/')[-tail.size, 2].join(':').sub(/_generator\.rb$/, '')
+ end
+ tail.shift
+ end
+ end
+
+ others.sort!
+ puts "Others: #{others.join(', ')}." unless others.empty?
+ end
+
+ protected
+
+ # Return all defined namespaces.
+ #
+ def self.namespaces #:nodoc:
+ Thor::Base.subclasses.map{ |klass| klass.namespace }
+ end
+
+ # Keep builtin generators in an Array[Array[group, name]].
+ #
+ def self.builtin #:nodoc:
+ Dir[File.dirname(__FILE__) + '/generators/*/*'].collect do |file|
+ file.split('/')[-2, 2]
+ end
+ end
+
+ # Try callbacks for the given base.
+ #
+ def self.invoke_fallbacks_for(name, base)
+ return nil unless base && fallbacks[base.to_sym]
+
+ invoked_fallbacks = []
+
+ Array(fallbacks[base.to_sym]).each do |fallback|
+ next if invoked_fallbacks.include?(fallback)
+ invoked_fallbacks << fallback
+
+ klass = find_by_namespace(name, fallback)
+ return klass if klass
+ end
+
+ nil
+ end
+
+ # Receives namespaces in an array and tries to find matching generators
+ # in the load path. Each path is traversed into directory lookups. For
+ # example:
+ #
+ # rails:generators:model
+ #
+ # Becomes:
+ #
+ # generators/rails/model/model_generator.rb
+ # generators/rails/model_generator.rb
+ # generators/model_generator.rb
+ #
+ def self.lookup(attempts) #:nodoc:
+ attempts.each do |attempt|
+ generators_path = ['.']
+
+ paths = attempt.gsub(':generators:', ':').split(':')
+ name = "#{paths.last}_generator.rb"
+
+ until paths.empty?
+ generators_path.unshift File.join(*paths)
+ paths.pop
+ end
+
+ generators_path.uniq!
+ generators_path = "{#{generators_path.join(',')}}"
+
+ self.load_path.each do |path|
+ Dir[File.join(path, generators_path, name)].each do |file|
+ begin
+ require file
+ rescue Exception => e
+ warn "[WARNING] Could not load generator at #{file.inspect}. Error: #{e.message}"
+ end
+ end
+ end
+ end
+ end
+
+ end
+end
+
diff --git a/railties/lib/generators/action_orm.rb b/railties/lib/generators/action_orm.rb
new file mode 100644
index 0000000000..69cf227fd7
--- /dev/null
+++ b/railties/lib/generators/action_orm.rb
@@ -0,0 +1,74 @@
+module Rails
+ module Generators
+ # ActionORM is a class to be implemented by each ORM to allow Rails to
+ # generate customized controller code.
+ #
+ # The API has the same methods as ActiveRecord, but each method returns a
+ # string that matches the ORM API.
+ #
+ # For example:
+ #
+ # ActiveRecord::Generators::ActionORM.find(Foo, "params[:id]")
+ # #=> "Foo.find(params[:id])"
+ #
+ # Datamapper::Generators::ActionORM.find(Foo, "params[:id]")
+ # #=> "Foo.get(params[:id])"
+ #
+ # On initialization, the ActionORM accepts the instance name that will
+ # receive the calls:
+ #
+ # builder = ActiveRecord::Generators::ActionORM.new "@foo"
+ # builder.save #=> "@foo.save"
+ #
+ # The only exception in ActionORM for ActiveRecord is the use of self.build
+ # instead of self.new.
+ #
+ class ActionORM
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ # GET index
+ def self.all(klass)
+ raise NotImplementedError
+ end
+
+ # GET show
+ # GET edit
+ # PUT update
+ # DELETE destroy
+ def self.find(klass, params=nil)
+ raise NotImplementedError
+ end
+
+ # GET new
+ # POST create
+ def self.build(klass, params=nil)
+ raise NotImplementedError
+ end
+
+ # POST create
+ def save
+ raise NotImplementedError
+ end
+
+ # PUT update
+ def update_attributes(params=nil)
+ raise NotImplementedError
+ end
+
+ # POST create
+ # PUT update
+ def errors
+ raise NotImplementedError
+ end
+
+ # DELETE destroy
+ def destroy
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/railties/lib/generators/actions.rb b/railties/lib/generators/actions.rb
new file mode 100644
index 0000000000..55ef212abb
--- /dev/null
+++ b/railties/lib/generators/actions.rb
@@ -0,0 +1,263 @@
+require 'open-uri'
+
+module Rails
+ module Generators
+ module Actions
+
+ # Install a plugin. You must provide either a Subversion url or Git url.
+ # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned.
+ #
+ # ==== Examples
+ #
+ # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git'
+ # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true
+ # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
+ #
+ def plugin(name, options)
+ log :plugin, name
+
+ if options[:git] && options[:submodule]
+ in_root do
+ run "git submodule add #{options[:git]} vendor/plugins/#{name}", :verbose => false
+ end
+ elsif options[:git] || options[:svn]
+ in_root do
+ run_ruby_script "script/plugin install #{options[:svn] || options[:git]}", :verbose => false
+ end
+ else
+ log "! no git or svn provided for #{name}. Skipping..."
+ end
+ end
+
+ # Adds an entry into config/environment.rb for the supplied gem. If env
+ # is specified, add the gem to the given environment.
+ #
+ # ==== Example
+ #
+ # gem "rspec", :env => :test
+ # gem "technoweenie-restful-authentication", :lib => "restful-authentication", :source => "http://gems.github.com/"
+ #
+ def gem(name, options={})
+ log :gem, name
+ env = options.delete(:env)
+
+ gems_code = "config.gem '#{name}'"
+
+ if options.any?
+ opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ")
+ gems_code << ", #{opts}"
+ end
+
+ environment gems_code, :env => env
+ end
+
+ # Adds a line inside the Initializer block for config/environment.rb.
+ #
+ # If options :env is specified, the line is appended to the corresponding
+ # file in config/environments.
+ #
+ def environment(data=nil, options={}, &block)
+ sentinel = "Rails::Initializer.run do |config|"
+ data = block.call if !data && block_given?
+
+ in_root do
+ if options[:env].nil?
+ inject_into_file 'config/environment.rb', "\n #{data}", :after => sentinel, :verbose => false
+ else
+ Array.wrap(options[:env]).each do|env|
+ append_file "config/environments/#{env}.rb", "\n#{data}", :verbose => false
+ end
+ end
+ end
+ end
+
+ # Run a command in git.
+ #
+ # ==== Examples
+ #
+ # git :init
+ # git :add => "this.file that.rb"
+ # git :add => "onefile.rb", :rm => "badfile.cxx"
+ #
+ def git(command={})
+ in_root do
+ if command.is_a?(Symbol)
+ run "git #{command}"
+ else
+ command.each do |command, options|
+ run "git #{command} #{options}"
+ end
+ end
+ end
+ end
+
+ # Create a new file in the vendor/ directory. Code can be specified
+ # in a block or a data string can be given.
+ #
+ # ==== Examples
+ #
+ # vendor("sekrit.rb") do
+ # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--"
+ # "salt = '#{sekrit_salt}'"
+ # end
+ #
+ # vendor("foreign.rb", "# Foreign code is fun")
+ #
+ def vendor(filename, data=nil, &block)
+ log :vendor, filename
+ create_file("vendor/#{filename}", data, :verbose => false, &block)
+ end
+
+ # Create a new file in the lib/ directory. Code can be specified
+ # in a block or a data string can be given.
+ #
+ # ==== Examples
+ #
+ # lib("crypto.rb") do
+ # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
+ # end
+ #
+ # lib("foreign.rb", "# Foreign code is fun")
+ #
+ def lib(filename, data=nil, &block)
+ log :lib, filename
+ create_file("lib/#{filename}", data, :verbose => false, &block)
+ end
+
+ # Create a new Rakefile with the provided code (either in a block or a string).
+ #
+ # ==== Examples
+ #
+ # rakefile("bootstrap.rake") do
+ # project = ask("What is the UNIX name of your project?")
+ #
+ # <<-TASK
+ # namespace :#{project} do
+ # task :bootstrap do
+ # puts "i like boots!"
+ # end
+ # end
+ # TASK
+ # end
+ #
+ # rakefile("seed.rake", "puts 'im plantin ur seedz'")
+ #
+ def rakefile(filename, data=nil, &block)
+ log :rakefile, filename
+ create_file("lib/tasks/#{filename}", data, :verbose => false, &block)
+ end
+
+ # Create a new initializer with the provided code (either in a block or a string).
+ #
+ # ==== Examples
+ #
+ # initializer("globals.rb") do
+ # data = ""
+ #
+ # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do
+ # data << "#{const} = :entp"
+ # end
+ #
+ # data
+ # end
+ #
+ # initializer("api.rb", "API_KEY = '123456'")
+ #
+ def initializer(filename, data=nil, &block)
+ log :initializer, filename
+ create_file("config/initializers/#{filename}", data, :verbose => false, &block)
+ end
+
+ # Generate something using a generator from Rails or a plugin.
+ # The second parameter is the argument string that is passed to
+ # the generator or an Array that is joined.
+ #
+ # ==== Example
+ #
+ # generate(:authenticated, "user session")
+ #
+ def generate(what, *args)
+ log :generate, what
+ argument = args.map {|arg| arg.to_s }.flatten.join(" ")
+
+ in_root { run_ruby_script("script/generate #{what} #{argument}", :verbose => false) }
+ end
+
+ # Runs the supplied rake task
+ #
+ # ==== Example
+ #
+ # rake("db:migrate")
+ # rake("db:migrate", :env => "production")
+ # rake("gems:install", :sudo => true)
+ #
+ def rake(command, options={})
+ log :rake, command
+ env = options[:env] || 'development'
+ sudo = options[:sudo] && RUBY_PLATFORM !~ /mswin|mingw/ ? 'sudo ' : ''
+ in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", :verbose => false) }
+ end
+
+ # Just run the capify command in root
+ #
+ # ==== Example
+ #
+ # capify!
+ #
+ def capify!
+ log :capify, ""
+ in_root { run("#{extify(:capify)} .", :verbose => false) }
+ end
+
+ # Add Rails to /vendor/rails
+ #
+ # ==== Example
+ #
+ # freeze!
+ #
+ def freeze!(args = {})
+ log :vendor, "rails"
+ in_root { run("#{extify(:rake)} rails:freeze:edge", :verbose => false) }
+ end
+
+ # Make an entry in Rails routing file conifg/routes.rb
+ #
+ # === Example
+ #
+ # route "map.root :controller => :welcome"
+ #
+ def route(routing_code)
+ log :route, routing_code
+ sentinel = "ActionController::Routing::Routes.draw do |map|"
+
+ in_root do
+ inject_into_file 'config/routes.rb', "\n #{routing_code}\n", { :after => sentinel, :verbose => false }
+ end
+ end
+
+ protected
+
+ # Define log for backwards compatibility. If just one argument is sent,
+ # invoke say, otherwise invoke say_status.
+ #
+ def log(*args)
+ if args.size == 1
+ say args.first.to_s
+ else
+ say_status *args
+ end
+ end
+
+ # Add an extension to the given name based on the platform.
+ #
+ def extify(name)
+ if RUBY_PLATFORM =~ /mswin|mingw/
+ "#{name}.bat"
+ else
+ name
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/generators/active_record.rb b/railties/lib/generators/active_record.rb
new file mode 100644
index 0000000000..64bee3904e
--- /dev/null
+++ b/railties/lib/generators/active_record.rb
@@ -0,0 +1,57 @@
+require 'generators/named_base'
+require 'generators/migration'
+require 'generators/action_orm'
+require 'active_record'
+
+module ActiveRecord
+ module Generators
+ class Base < Rails::Generators::NamedBase #:nodoc:
+ include Rails::Generators::Migration
+
+ protected
+ # Implement the required interface for Rails::Generators::Migration.
+ #
+ def next_migration_number(dirname) #:nodoc:
+ if ActiveRecord::Base.timestamped_migrations
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
+ else
+ "%.3d" % (current_migration_number(dirname) + 1)
+ end
+ end
+ end
+
+ class ActionORM < Rails::Generators::ActionORM #:nodoc:
+ def self.all(klass)
+ "#{klass}.all"
+ end
+
+ def self.find(klass, params=nil)
+ "#{klass}.find(#{params})"
+ end
+
+ def self.build(klass, params=nil)
+ if params
+ "#{klass}.new(#{params})"
+ else
+ "#{klass}.new"
+ end
+ end
+
+ def save
+ "#{name}.save"
+ end
+
+ def update_attributes(params=nil)
+ "#{name}.update_attributes(#{params})"
+ end
+
+ def errors
+ "#{name}.errors"
+ end
+
+ def destroy
+ "#{name}.destroy"
+ end
+ end
+ end
+end
diff --git a/railties/lib/generators/active_record/migration/migration_generator.rb b/railties/lib/generators/active_record/migration/migration_generator.rb
new file mode 100644
index 0000000000..7939977f72
--- /dev/null
+++ b/railties/lib/generators/active_record/migration/migration_generator.rb
@@ -0,0 +1,25 @@
+require 'generators/active_record'
+
+module ActiveRecord
+ module Generators
+ class MigrationGenerator < Base
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+
+ def create_migration_file
+ set_local_assigns!
+ migration_template "migration.rb", "db/migrate/#{file_name}.rb"
+ end
+
+ protected
+ attr_reader :migration_action
+
+ def set_local_assigns!
+ if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/
+ @migration_action = $1
+ @table_name = $2.pluralize
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/migration/templates/migration.rb b/railties/lib/generators/active_record/migration/templates/migration.rb
index ca35a43229..bbb7c53d86 100644
--- a/railties/lib/rails_generator/generators/components/migration/templates/migration.rb
+++ b/railties/lib/generators/active_record/migration/templates/migration.rb
@@ -1,4 +1,4 @@
-class <%= class_name.underscore.camelize %> < ActiveRecord::Migration
+class <%= migration_class_name %> < ActiveRecord::Migration
def self.up<% attributes.each do |attribute| %>
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%>
<%- end %>
diff --git a/railties/lib/generators/active_record/model/model_generator.rb b/railties/lib/generators/active_record/model/model_generator.rb
new file mode 100644
index 0000000000..54187aede0
--- /dev/null
+++ b/railties/lib/generators/active_record/model/model_generator.rb
@@ -0,0 +1,35 @@
+require 'generators/active_record'
+
+module ActiveRecord
+ module Generators
+ class ModelGenerator < Base
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+
+ check_class_collision
+
+ class_option :migration, :type => :boolean
+ class_option :timestamps, :type => :boolean
+ class_option :parent, :type => :string, :desc => "The parent class for the generated model"
+
+ def create_migration_file
+ if options[:migration] && options[:parent].nil?
+ file_name = "create_#{file_path.gsub(/\//, '_').pluralize}"
+ migration_template "migration.rb", "db/migrate/#{file_name}.rb"
+ end
+ end
+
+ def create_model_file
+ template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
+ end
+
+ hook_for :test_framework
+
+ protected
+
+ def parent_class_name
+ options[:parent] || "ActiveRecord::Base"
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/model/templates/migration.rb b/railties/lib/generators/active_record/model/templates/migration.rb
index 382fd1156e..1f68487304 100644
--- a/railties/lib/rails_generator/generators/components/model/templates/migration.rb
+++ b/railties/lib/generators/active_record/model/templates/migration.rb
@@ -1,10 +1,10 @@
-class <%= migration_name %> < ActiveRecord::Migration
+class <%= migration_class_name %> < ActiveRecord::Migration
def self.up
create_table :<%= table_name %> do |t|
<% for attribute in attributes -%>
t.<%= attribute.type %> :<%= attribute.name %>
<% end -%>
-<% unless options[:skip_timestamps] %>
+<% if options[:timestamps] %>
t.timestamps
<% end -%>
end
diff --git a/railties/lib/rails_generator/generators/components/model/templates/model.rb b/railties/lib/generators/active_record/model/templates/model.rb
index 0656b06dfe..21ae29e9f2 100644
--- a/railties/lib/rails_generator/generators/components/model/templates/model.rb
+++ b/railties/lib/generators/active_record/model/templates/model.rb
@@ -1,4 +1,4 @@
-class <%= class_name %> < ActiveRecord::Base
+class <%= class_name %> < <%= parent_class_name.classify %>
<% attributes.select {|attr| attr.reference? }.each do |attribute| -%>
belongs_to :<%= attribute.name %>
<% end -%>
diff --git a/railties/lib/generators/active_record/observer/observer_generator.rb b/railties/lib/generators/active_record/observer/observer_generator.rb
new file mode 100644
index 0000000000..a6b57423b8
--- /dev/null
+++ b/railties/lib/generators/active_record/observer/observer_generator.rb
@@ -0,0 +1,15 @@
+require 'generators/active_record'
+
+module ActiveRecord
+ module Generators
+ class ObserverGenerator < Base
+ check_class_collision :suffix => "Observer"
+
+ def create_observer_file
+ template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb")
+ end
+
+ hook_for :test_framework
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/observer/templates/observer.rb b/railties/lib/generators/active_record/observer/templates/observer.rb
index b9a3004161..b9a3004161 100644
--- a/railties/lib/rails_generator/generators/components/observer/templates/observer.rb
+++ b/railties/lib/generators/active_record/observer/templates/observer.rb
diff --git a/railties/lib/generators/active_record/session_migration/session_migration_generator.rb b/railties/lib/generators/active_record/session_migration/session_migration_generator.rb
new file mode 100644
index 0000000000..d60da5c0a5
--- /dev/null
+++ b/railties/lib/generators/active_record/session_migration/session_migration_generator.rb
@@ -0,0 +1,20 @@
+require 'generators/active_record'
+
+module ActiveRecord
+ module Generators
+ class SessionMigrationGenerator < Base
+ argument :name, :type => :string, :default => "add_sessions_table"
+
+ def create_migration_file
+ migration_template "migration.rb", "db/migrate/#{file_name}.rb"
+ end
+
+ protected
+
+ def session_table_name
+ ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session'
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb b/railties/lib/generators/active_record/session_migration/templates/migration.rb
index ca220a5f23..919822af7b 100644
--- a/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb
+++ b/railties/lib/generators/active_record/session_migration/templates/migration.rb
@@ -1,4 +1,4 @@
-class <%= class_name %> < ActiveRecord::Migration
+class <%= migration_class_name %> < ActiveRecord::Migration
def self.up
create_table :<%= session_table_name %> do |t|
t.string :session_id, :null => false
diff --git a/railties/lib/generators/base.rb b/railties/lib/generators/base.rb
new file mode 100644
index 0000000000..cbe9c0a49d
--- /dev/null
+++ b/railties/lib/generators/base.rb
@@ -0,0 +1,302 @@
+require 'generators/actions'
+
+module Rails
+ module Generators
+ class Error < Thor::Error
+ end
+
+ class Base < Thor::Group
+ include Thor::Actions
+ include Rails::Generators::Actions
+
+ # Automatically sets the source root based on the class name.
+ #
+ def self.source_root
+ @_rails_source_root ||= File.expand_path(File.join(File.dirname(__FILE__),
+ base_name, generator_name, 'templates'))
+ end
+
+ # Tries to get the description from a USAGE file one folder above the source
+ # root otherwise uses a default description.
+ #
+ def self.desc(description=nil)
+ return super if description
+ usage = File.expand_path(File.join(source_root, "..", "USAGE"))
+
+ @desc ||= if File.exist?(usage)
+ File.read(usage)
+ else
+ "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
+ end
+ end
+
+ # Convenience method to get the namespace from the class name. It's the
+ # same as Thor default except that the Generator at the end of the class
+ # is removed.
+ #
+ def self.namespace(name=nil)
+ return super if name
+ @namespace ||= super.sub(/_generator$/, '')
+ end
+
+ # Invoke a generator based on the value supplied by the user to the
+ # given option named "name". A class option is created when this method
+ # is invoked and you can set a hash to customize it.
+ #
+ # ==== Examples
+ #
+ # class ControllerGenerator < Rails::Generators::Base
+ # hook_for :test_framework, :aliases => "-t"
+ # end
+ #
+ # The example above will create a test framework option and will invoke
+ # a generator based on the user supplied value.
+ #
+ # For example, if the user invoke the controller generator as:
+ #
+ # ruby script/generate controller Account --test-framework=test_unit
+ #
+ # The controller generator will then try to invoke the following generators:
+ #
+ # "rails:generators:test_unit", "test_unit:generators:controller", "test_unit"
+ #
+ # In this case, the "test_unit:generators:controller" is available and is
+ # invoked. This allows any test framework to hook into Rails as long as it
+ # provides any of the hooks above.
+ #
+ # Finally, if the user don't want to use any test framework, he can do:
+ #
+ # ruby script/generate controller Account --skip-test-framework
+ #
+ # Or similarly:
+ #
+ # ruby script/generate controller Account --no-test-framework
+ #
+ # ==== Boolean hooks
+ #
+ # In some cases, you want to provide a boolean hook. For example, webrat
+ # developers might want to have webrat available on controller generator.
+ # This can be achieved as:
+ #
+ # Rails::Generators::ControllerGenerator.hook_for :webrat, :type => :boolean
+ #
+ # Then, if you want, webrat to be invoked, just supply:
+ #
+ # ruby script/generate controller Account --webrat
+ #
+ # The hooks lookup is similar as above:
+ #
+ # "rails:generators:webrat", "webrat:generators:controller", "webrat"
+ #
+ # ==== Custom invocations
+ #
+ # You can also supply a block to hook_for to customize how the hook is
+ # going to be invoked. The block receives two parameters, an instance
+ # of the current class and the klass to be invoked.
+ #
+ # For example, in the resource generator, the controller should be invoked
+ # with a pluralized class name. By default, it is invoked with the same
+ # name as the resource generator, which is singular. To change this, we
+ # can give a block to customize how the controller can be invoked.
+ #
+ # hook_for :resource_controller do |instance, controller|
+ # instance.invoke controller, [ instance.name.pluralize ]
+ # end
+ #
+ def self.hook_for(*names, &block)
+ options = names.extract_options!
+ in_base = options.delete(:in) || base_name
+ as_hook = options.delete(:as) || generator_name
+
+ names.each do |name|
+ defaults = if options[:type] == :boolean
+ { }
+ elsif [true, false].include?(default_value_for_option(name, options))
+ { :banner => "" }
+ else
+ { :desc => "#{name.to_s.humanize} to be invoked", :banner => "NAME" }
+ end
+
+ unless class_options.key?(name)
+ class_option name, defaults.merge!(options)
+ end
+
+ hooks[name] = [ in_base, as_hook ]
+ invoke_from_option name, options, &block
+ end
+ end
+
+ # Remove a previously added hook.
+ #
+ # ==== Examples
+ #
+ # remove_hook_for :orm
+ #
+ def self.remove_hook_for(*names)
+ remove_invocation *names
+
+ names.each do |name|
+ hooks.delete(name)
+ end
+ end
+
+ # Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
+ #
+ def self.class_option(name, options={}) #:nodoc:
+ options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
+ options[:aliases] = default_aliases_for_option(name, options)
+ options[:default] = default_value_for_option(name, options)
+ super(name, options)
+ end
+
+ # Cache source root and add lib/generators/base/generator/templates to
+ # source paths.
+ #
+ def self.inherited(base) #:nodoc:
+ super
+ base.source_root # Cache source root
+
+ if defined?(RAILS_ROOT) && base.name !~ /Base$/
+ path = File.expand_path(File.join(RAILS_ROOT, 'lib', 'templates'))
+ if base.name.include?('::')
+ base.source_paths << File.join(path, base.base_name, base.generator_name)
+ else
+ base.source_paths << File.join(path, base.generator_name)
+ end
+ end
+ end
+
+ protected
+
+ # Check whether the given class names are already taken by user
+ # application or Ruby on Rails.
+ #
+ def class_collisions(*class_names) #:nodoc:
+ return unless behavior == :invoke
+
+ class_names.flatten.each do |class_name|
+ class_name = class_name.to_s
+ next if class_name.strip.empty?
+
+ # Split the class from its module nesting
+ nesting = class_name.split('::')
+ last_name = nesting.pop
+
+ # Hack to limit const_defined? to non-inherited on 1.9
+ extra = []
+ extra << false unless Object.method(:const_defined?).arity == 1
+
+ # Extract the last Module in the nesting
+ last = nesting.inject(Object) do |last, nest|
+ break unless last.const_defined?(nest, *extra)
+ last.const_get(nest)
+ end
+
+ if last && last.const_defined?(last_name.camelize, *extra)
+ raise Error, "The name '#{class_name}' is either already used in your application " <<
+ "or reserved by Ruby on Rails. Please choose an alternative and run " <<
+ "this generator again."
+ end
+ end
+ end
+
+ # Use Rails default banner.
+ #
+ def self.banner
+ "#{$0} #{generator_name} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]"
+ end
+
+ # Sets the base_name taking into account the current class namespace.
+ #
+ def self.base_name
+ @base_name ||= self.name.split('::').first.underscore
+ end
+
+ # Removes the namespaces and get the generator name. For example,
+ # Rails::Generators::MetalGenerator will return "metal" as generator name.
+ #
+ def self.generator_name
+ @generator_name ||= begin
+ klass_name = self.name.split('::').last
+ klass_name.sub!(/Generator$/, '')
+ klass_name.underscore
+ end
+ end
+
+ # Return the default value for the option name given doing a lookup in
+ # Rails::Generators.options.
+ #
+ def self.default_value_for_option(name, options)
+ config = Rails::Generators.options
+ generator, base = generator_name.to_sym, base_name.to_sym
+
+ if config[generator] && config[generator].key?(name)
+ config[generator][name]
+ elsif config[base] && config[base].key?(name)
+ config[base][name]
+ elsif config[:rails].key?(name)
+ config[:rails][name]
+ else
+ options[:default]
+ end
+ end
+
+ # Return default aliases for the option name given doing a lookup in
+ # Rails::Generators.aliases.
+ #
+ def self.default_aliases_for_option(name, options)
+ config = Rails::Generators.aliases
+ generator, base = generator_name.to_sym, base_name.to_sym
+
+ if config[generator] && config[generator].key?(name)
+ config[generator][name]
+ elsif config[base] && config[base].key?(name)
+ config[base][name]
+ elsif config[:rails].key?(name)
+ config[:rails][name]
+ else
+ options[:aliases]
+ end
+ end
+
+ # Keep hooks configuration that are used on prepare_for_invocation.
+ #
+ def self.hooks #:nodoc:
+ @hooks ||= from_superclass(:hooks, {})
+ end
+
+ # Prepare class invocation to search on Rails namespace if a previous
+ # added hook is being used.
+ #
+ def self.prepare_for_invocation(name, value) #:nodoc:
+ if value && constants = self.hooks[name]
+ Rails::Generators.find_by_namespace(value, *constants)
+ else
+ super
+ end
+ end
+
+ # Small macro to add ruby as an option to the generator with proper
+ # default value plus an instance helper method called shebang.
+ #
+ def self.add_shebang_option!
+ class_option :ruby, :type => :string, :aliases => "-r", :default => Thor::Util.ruby_command,
+ :desc => "Path to the Ruby binary of your choice", :banner => "PATH"
+
+ no_tasks {
+ define_method :shebang do
+ @shebang ||= begin
+ command = if options[:ruby] == Thor::Util.ruby_command
+ "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}"
+ else
+ options[:ruby]
+ end
+ "#!#{command}"
+ end
+ end
+ }
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/generators/erb.rb b/railties/lib/generators/erb.rb
new file mode 100644
index 0000000000..43d47109c7
--- /dev/null
+++ b/railties/lib/generators/erb.rb
@@ -0,0 +1,8 @@
+require 'generators/named_base'
+
+module Erb
+ module Generators
+ class Base < Rails::Generators::NamedBase #:nodoc:
+ end
+ end
+end
diff --git a/railties/lib/generators/erb/controller/controller_generator.rb b/railties/lib/generators/erb/controller/controller_generator.rb
new file mode 100644
index 0000000000..ab7b273662
--- /dev/null
+++ b/railties/lib/generators/erb/controller/controller_generator.rb
@@ -0,0 +1,21 @@
+require 'generators/erb'
+
+module Erb
+ module Generators
+ class ControllerGenerator < Base
+ argument :actions, :type => :array, :default => [], :banner => "action action"
+
+ def create_view_files
+ base_path = File.join("app/views", class_path, file_name)
+ empty_directory base_path
+
+ actions.each do |action|
+ @action = action
+ @path = File.join(base_path, "#{action}.html.erb")
+
+ template 'view.html.erb', @path
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/generators/erb/controller/templates/view.html.erb b/railties/lib/generators/erb/controller/templates/view.html.erb
new file mode 100644
index 0000000000..cd54d13d83
--- /dev/null
+++ b/railties/lib/generators/erb/controller/templates/view.html.erb
@@ -0,0 +1,2 @@
+<h1><%= class_name %>#<%= @action %></h1>
+<p>Find me in <%= @path %></p>
diff --git a/railties/lib/generators/erb/mailer/mailer_generator.rb b/railties/lib/generators/erb/mailer/mailer_generator.rb
new file mode 100644
index 0000000000..4ec2f4c9f4
--- /dev/null
+++ b/railties/lib/generators/erb/mailer/mailer_generator.rb
@@ -0,0 +1,20 @@
+require 'generators/erb'
+
+module Erb
+ module Generators
+ class MailerGenerator < Base
+ argument :actions, :type => :array, :default => [], :banner => "method method"
+
+ def create_view_folder
+ empty_directory File.join("app/views", file_path)
+ end
+
+ def create_view_files
+ actions.each do |action|
+ @action, @path = action, File.join(file_path, action)
+ template "view.erb", File.join("app/views", "#{@path}.erb")
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/generators/erb/mailer/templates/view.erb b/railties/lib/generators/erb/mailer/templates/view.erb
new file mode 100644
index 0000000000..fcce7bd805
--- /dev/null
+++ b/railties/lib/generators/erb/mailer/templates/view.erb
@@ -0,0 +1,3 @@
+<%= class_name %>#<%= @action %>
+
+Find me in app/views/<%= @path %>
diff --git a/railties/lib/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/generators/erb/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..955f22192a
--- /dev/null
+++ b/railties/lib/generators/erb/scaffold/scaffold_generator.rb
@@ -0,0 +1,54 @@
+require 'generators/erb'
+
+module Erb
+ module Generators
+ class ScaffoldGenerator < Base
+ include Rails::Generators::ScaffoldBase
+
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+
+ class_option :form, :type => :boolean
+ class_option :layout, :type => :boolean
+ class_option :singleton, :type => :boolean, :desc => "Supply to skip index view"
+
+ def create_root_folder
+ empty_directory File.join("app/views", controller_file_path)
+ end
+
+ def copy_index_file
+ return if options[:singleton]
+ copy_view :index
+ end
+
+ def copy_edit_file
+ copy_view :edit
+ end
+
+ def copy_show_file
+ copy_view :show
+ end
+
+ def copy_new_file
+ copy_view :new
+ end
+
+ def copy_form_file
+ return unless options[:form]
+ copy_view :_form
+ end
+
+ def copy_layout_file
+ return unless options[:layout]
+ template "layout.html.erb",
+ File.join("app/views/layouts", controller_class_path, "#{controller_file_name}.html.erb")
+ end
+
+ protected
+
+ def copy_view(view)
+ template "#{view}.html.erb", File.join("app/views", controller_file_path, "#{view}.html.erb")
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb b/railties/lib/generators/erb/scaffold/templates/edit.html.erb
index cca1d61c68..cca1d61c68 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/edit.html.erb
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb b/railties/lib/generators/erb/scaffold/templates/index.html.erb
index 69beb388db..5e6a4af9e0 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/index.html.erb
@@ -3,7 +3,7 @@
<table>
<tr>
<% for attribute in attributes -%>
- <th><%= attribute.column.human_name %></th>
+ <th><%= attribute.human_name %></th>
<% end -%>
<th></th>
<th></th>
@@ -24,4 +24,4 @@
<br />
-<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %> \ No newline at end of file
+<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %>
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb b/railties/lib/generators/erb/scaffold/templates/layout.html.erb
index aacfbe4a8f..aacfbe4a8f 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/layout.html.erb
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb b/railties/lib/generators/erb/scaffold/templates/new.html.erb
index 96c89fc50e..96c89fc50e 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/new.html.erb
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb b/railties/lib/generators/erb/scaffold/templates/show.html.erb
index adecaf70c6..25567957be 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/show.html.erb
@@ -1,10 +1,10 @@
<% for attribute in attributes -%>
<p>
- <b><%= attribute.column.human_name %>:</b>
+ <b><%= attribute.human_name %>:</b>
<%%=h @<%= singular_name %>.<%= attribute.name %> %>
</p>
<% end -%>
<%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> |
-<%%= link_to 'Back', <%= plural_name %>_path %> \ No newline at end of file
+<%%= link_to 'Back', <%= plural_name %>_path %>
diff --git a/railties/lib/rails_generator/generated_attribute.rb b/railties/lib/generators/generated_attribute.rb
index a3d4a01142..e962308585 100644
--- a/railties/lib/rails_generator/generated_attribute.rb
+++ b/railties/lib/generators/generated_attribute.rb
@@ -1,13 +1,10 @@
-require 'optparse'
-
module Rails
- module Generator
+ module Generators
class GeneratedAttribute
- attr_accessor :name, :type, :column
+ attr_accessor :name, :type
def initialize(name, type)
@name, @type = name, type.to_sym
- @column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, @type)
end
def field_type
@@ -20,7 +17,7 @@ module Rails
when :boolean then :check_box
else
:text_field
- end
+ end
end
def default
@@ -33,9 +30,14 @@ module Rails
when :string then "MyString"
when :text then "MyText"
when :boolean then false
+ when :references, :belongs_to then nil
else
""
- end
+ end
+ end
+
+ def human_name
+ name.to_s.humanize
end
def reference?
diff --git a/railties/lib/generators/migration.rb b/railties/lib/generators/migration.rb
new file mode 100644
index 0000000000..0a9151ecdf
--- /dev/null
+++ b/railties/lib/generators/migration.rb
@@ -0,0 +1,65 @@
+module Rails
+ module Generators
+ # Holds common methods for migrations. It assumes that migrations has the
+ # [0-9]*_name format and can be used by another frameworks (like Sequel)
+ # just by implementing the next migration number method.
+ #
+ module Migration
+ def self.included(base) #:nodoc:
+ base.send :attr_reader, :migration_number,
+ :migration_file_name,
+ :migration_class_name
+ end
+
+ # Creates a migration template at the given destination. The difference
+ # to the default template method is that the migration number is appended
+ # to the destination file name.
+ #
+ # The migration number, migration file name, migration class name are
+ # available as instance variables in the template to be rendered.
+ #
+ # ==== Examples
+ #
+ # migration_template "migration.rb", "db/migrate/add_foo_to_bar.rb"
+ #
+ def migration_template(source, destination=nil, config={})
+ destination = File.expand_path(destination || source, self.destination_root)
+
+ migration_dir = File.dirname(destination)
+ @migration_number = next_migration_number(migration_dir)
+ @migration_file_name = File.basename(destination).sub(/\.rb$/, '')
+ @migration_class_name = @migration_file_name.camelize
+
+ destination = migration_exists?(migration_dir, @migration_file_name)
+
+ if behavior == :invoke
+ raise Error, "Another migration is already named #{@migration_file_name}: #{destination}" if destination
+ destination = File.join(migration_dir, "#{@migration_number}_#{@migration_file_name}.rb")
+ end
+
+ template(source, destination, config)
+ end
+
+ protected
+
+ def migration_lookup_at(dirname) #:nodoc:
+ Dir.glob("#{dirname}/[0-9]*_*.rb")
+ end
+
+ def migration_exists?(dirname, file_name) #:nodoc:
+ migration_lookup_at(dirname).grep(/\d+_#{file_name}.rb$/).first
+ end
+
+ def current_migration_number(dirname) #:nodoc:
+ migration_lookup_at(dirname).collect do |file|
+ File.basename(file).split("_").first.to_i
+ end.max.to_i
+ end
+
+ def next_migration_number(dirname) #:nodoc:
+ raise NotImplementError
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/generators/named_base.rb b/railties/lib/generators/named_base.rb
new file mode 100644
index 0000000000..699b8ed651
--- /dev/null
+++ b/railties/lib/generators/named_base.rb
@@ -0,0 +1,162 @@
+require 'generators/base'
+require 'generators/generated_attribute'
+
+module Rails
+ module Generators
+ class NamedBase < Base
+ argument :name, :type => :string
+
+ attr_reader :class_name, :singular_name, :plural_name, :table_name,
+ :class_path, :file_path, :class_nesting_depth
+
+ alias :file_name :singular_name
+
+ def initialize(*args) #:nodoc:
+ super
+ assign_names!(self.name)
+ parse_attributes! if respond_to?(:attributes)
+ end
+
+ protected
+
+ def assign_names!(given_name) #:nodoc:
+ base_name, @class_path, @file_path, class_nesting, @class_nesting_depth = extract_modules(given_name)
+ class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name)
+
+ @table_name = if pluralize_table_names?
+ plural_name
+ else
+ singular_name
+ end
+ @table_name.gsub! '/', '_'
+
+ if class_nesting.empty?
+ @class_name = class_name_without_nesting
+ else
+ @table_name = class_nesting.underscore << "_" << @table_name
+ @class_name = "#{class_nesting}::#{class_name_without_nesting}"
+ end
+ end
+
+ # Convert attributes hash into an array with GeneratedAttribute objects.
+ #
+ def parse_attributes! #:nodoc:
+ self.attributes = (attributes || []).map do |key_value|
+ name, type = key_value.split(':')
+ Rails::Generators::GeneratedAttribute.new(name, type)
+ end
+ end
+
+ # Extract modules from filesystem-style or ruby-style path. Both
+ # good/fun/stuff and Good::Fun::Stuff produce the same results.
+ #
+ def extract_modules(name) #:nodoc:
+ modules = name.include?('/') ? name.split('/') : name.split('::')
+ name = modules.pop
+ path = modules.map { |m| m.underscore }
+
+ file_path = (path + [name.underscore]).join('/')
+ nesting = modules.map { |m| m.camelize }.join('::')
+
+ [name, path, file_path, nesting, modules.size]
+ end
+
+ # Receives name and return camelized, underscored and pluralized names.
+ #
+ def inflect_names(name) #:nodoc:
+ camel = name.camelize
+ under = camel.underscore
+ plural = under.pluralize
+ [camel, under, plural]
+ end
+
+ def pluralize_table_names?
+ !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names
+ end
+
+ # Add a class collisions name to be checked on class initialization. You
+ # can supply a hash with a :prefix or :suffix to be tested.
+ #
+ # ==== Examples
+ #
+ # check_class_collision :suffix => "Observer"
+ #
+ # If the generator is invoked with class name Admin, it will check for
+ # the presence of "AdminObserver".
+ #
+ def self.check_class_collision(options={})
+ define_method :check_class_collision do
+ name = if self.respond_to?(:controller_class_name) # for ScaffoldBase
+ controller_class_name
+ else
+ class_name
+ end
+
+ class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}"
+ end
+ end
+ end
+
+ # Deal with controller names on scaffold. Also provide helpers to deal with
+ # ActionORM.
+ #
+ module ScaffoldBase
+ def self.included(base) #:nodoc:
+ base.send :attr_reader, :controller_name, :controller_class_name, :controller_file_name,
+ :controller_class_path, :controller_file_path
+ end
+
+ # Set controller variables on initialization.
+ #
+ def initialize(*args) #:nodoc:
+ super
+ @controller_name = name.pluralize
+
+ base_name, @controller_class_path, @controller_file_path, class_nesting, class_nesting_depth = extract_modules(@controller_name)
+ class_name_without_nesting, @controller_file_name, controller_plural_name = inflect_names(base_name)
+
+ @controller_class_name = if class_nesting.empty?
+ class_name_without_nesting
+ else
+ "#{class_nesting}::#{class_name_without_nesting}"
+ end
+ end
+
+ protected
+
+ # Loads the ORM::Generators::ActionORM class. This class is responsable
+ # to tell scaffold entities how to generate an specific method for the
+ # ORM. Check Rails::Generators::ActionORM for more information.
+ #
+ def orm_class
+ @orm_class ||= begin
+ # Raise an error if the class_option :orm was not defined.
+ unless self.class.class_options[:orm]
+ raise "You need to have :orm as class option to invoke orm_class and orm_instance"
+ end
+
+ action_orm = "#{options[:orm].to_s.classify}::Generators::ActionORM"
+
+ # If the orm was not loaded, try to load it at "generators/orm",
+ # for example "generators/active_record" or "generators/sequel".
+ begin
+ klass = action_orm.constantize
+ rescue NameError
+ require "generators/#{options[:orm]}"
+ end
+
+ # Try once again after loading the file with success.
+ klass ||= action_orm.constantize
+ rescue Exception => e
+ raise Error, "Could not load #{action_orm}, skipping controller. Error: #{e.message}."
+ end
+ end
+
+ # Initialize ORM::Generators::ActionORM to access instance methods.
+ #
+ def orm_instance(name=file_name)
+ @orm_instance ||= @orm_class.new(name)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/applications/app/USAGE b/railties/lib/generators/rails/app/USAGE
index 36d6061a59..36d6061a59 100644
--- a/railties/lib/rails_generator/generators/applications/app/USAGE
+++ b/railties/lib/generators/rails/app/USAGE
diff --git a/railties/lib/generators/rails/app/app_generator.rb b/railties/lib/generators/rails/app/app_generator.rb
new file mode 100644
index 0000000000..c8044d13b1
--- /dev/null
+++ b/railties/lib/generators/rails/app/app_generator.rb
@@ -0,0 +1,210 @@
+require 'digest/md5'
+require 'active_support/secure_random'
+require 'rails/version' unless defined?(Rails::VERSION)
+
+module Rails::Generators
+ class AppGenerator < Base
+ DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db )
+ add_shebang_option!
+
+ argument :app_path, :type => :string
+
+ class_option :database, :type => :string, :aliases => "-d", :default => "sqlite3",
+ :desc => "Preconfigure for selected database (options: #{DATABASES.join('/')})"
+
+ class_option :freeze, :type => :boolean, :aliases => "-F", :default => false,
+ :desc => "Freeze Rails in vendor/rails from the gems"
+
+ class_option :template, :type => :string, :aliases => "-m",
+ :desc => "Path to an application template (can be a filesystem path or URL)."
+
+ class_option :with_dispatchers, :type => :boolean, :aliases => "-D", :default => false,
+ :desc => "Add CGI/FastCGI/mod_ruby dispatchers code"
+
+ class_option :skip_activerecord, :type => :boolean, :aliases => "-O", :default => false,
+ :desc => "Skip ActiveRecord files"
+
+ class_option :skip_testunit, :type => :boolean, :aliases => "-T", :default => false,
+ :desc => "Skip TestUnit files"
+
+ class_option :skip_prototype, :type => :boolean, :aliases => "-J", :default => false,
+ :desc => "Skip Prototype files"
+
+ # Add Rails options
+ #
+ class_option :version, :type => :boolean, :aliases => "-v", :group => :rails,
+ :desc => "Show Rails version number and quit"
+
+ class_option :help, :type => :boolean, :aliases => "-h", :group => :rails,
+ :desc => "Show this help message and quit"
+
+ def initialize(*args)
+ super
+ if !options[:no_activerecord] && !DATABASES.include?(options[:database])
+ raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
+ end
+ end
+
+ def create_root
+ self.destination_root = File.expand_path(app_path, destination_root)
+ empty_directory '.'
+
+ app_name # Sets the app name
+ FileUtils.cd(destination_root)
+ end
+
+ def create_root_files
+ copy_file "Rakefile"
+ copy_file "README"
+ end
+
+ def create_app_files
+ directory "app"
+ end
+
+ def create_config_files
+ empty_directory "config"
+
+ inside "config" do
+ copy_file "routes.rb"
+ template "environment.rb"
+
+ directory "environments"
+ directory "initializers"
+ directory "locales"
+ end
+ end
+
+ def create_boot_file
+ copy_file "config/boot.rb"
+ end
+
+ def create_activerecord_files
+ return if options[:skip_activerecord]
+ template "config/databases/#{options[:database]}.yml", "config/database.yml"
+ end
+
+ def create_db_files
+ directory "db"
+ end
+
+ def create_doc_files
+ directory "doc"
+ end
+
+ def create_lib_files
+ empty_directory "lib"
+ empty_directory "lib/tasks"
+ end
+
+ def create_log_files
+ empty_directory "log"
+
+ inside "log" do
+ %w( server production development test ).each do |file|
+ create_file "#{file}.log"
+ chmod "#{file}.log", 0666, :verbose => false
+ end
+ end
+ end
+
+ def create_public_files
+ directory "public", "public", :recursive => false # Do small steps, so anyone can overwrite it.
+ end
+
+ def create_dispatch_files
+ return unless options[:with_dispatchers]
+ copy_file "dispatchers/config.ru", "config.ru"
+
+ template "dispatchers/dispatch.rb", "public/dispatch.rb"
+ chmod "public/dispatch.rb", 0755, :verbose => false
+
+ template "dispatchers/dispatch.rb", "public/dispatch.cgi"
+ chmod "public/dispatch.cgi", 0755, :verbose => false
+
+ template "dispatchers/dispatch.fcgi", "public/dispatch.fcgi"
+ chmod "public/dispatch.fcgi", 0755, :verbose => false
+ end
+
+ def create_public_image_files
+ directory "public/images"
+ end
+
+ def create_public_stylesheets_files
+ directory "public/stylesheets"
+ end
+
+ def create_prototype_files
+ return if options[:skip_prototype]
+ directory "public/javascripts"
+ end
+
+ def create_script_files
+ directory "script"
+ chmod "script", 0755, :verbose => false
+ end
+
+ def create_test_files
+ return if options[:skip_testunit]
+ directory "test"
+ end
+
+ def create_tmp_files
+ empty_directory "tmp"
+
+ inside "tmp" do
+ %w(sessions sockets cache pids).each do |dir|
+ empty_directory dir
+ end
+ end
+ end
+
+ def create_vendor_files
+ empty_directory "vendor/plugins"
+ end
+
+ def apply_rails_template
+ apply options[:template] if options[:template]
+ rescue Thor::Error, LoadError, Errno::ENOENT => e
+ raise Error, "The template [#{options[:template]}] could not be loaded. Error: #{e}"
+ end
+
+ def freeze?
+ freeze! if options[:freeze]
+ end
+
+ protected
+
+ # Define file as an alias to create_file for backwards compatibility.
+ #
+ def file(*args, &block)
+ create_file(*args, &block)
+ end
+
+ def app_name
+ @app_name ||= File.basename(destination_root)
+ end
+
+ def app_secret
+ ActiveSupport::SecureRandom.hex(64)
+ end
+
+ def self.banner
+ "#{$0} #{self.arguments.map(&:usage).join(' ')} [options]"
+ end
+
+ def mysql_socket
+ @mysql_socket ||= [
+ "/tmp/mysql.sock", # default
+ "/var/run/mysqld/mysqld.sock", # debian/gentoo
+ "/var/tmp/mysql.sock", # freebsd
+ "/var/lib/mysql/mysql.sock", # fedora
+ "/opt/local/lib/mysql/mysql.sock", # fedora
+ "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
+ "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
+ "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
+ "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
+ ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
+ end
+ end
+end
diff --git a/railties/lib/generators/rails/app/templates/README b/railties/lib/generators/rails/app/templates/README
new file mode 100644
index 0000000000..37ec8ea211
--- /dev/null
+++ b/railties/lib/generators/rails/app/templates/README
@@ -0,0 +1,243 @@
+== Welcome to Rails
+
+Rails is a web-application framework that includes everything needed to create
+database-backed web applications according to the Model-View-Control pattern.
+
+This pattern splits the view (also called the presentation) into "dumb" templates
+that are primarily responsible for inserting pre-built data in between HTML tags.
+The model contains the "smart" domain objects (such as Account, Product, Person,
+Post) that holds all the business logic and knows how to persist themselves to
+a database. The controller handles the incoming requests (such as Save New Account,
+Update Product, Show Post) by manipulating the model and directing data to the view.
+
+In Rails, the model is handled by what's called an object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in
+link:files/vendor/rails/activerecord/README.html.
+
+The controller and view are handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails. You can read more about Action Pack in
+link:files/vendor/rails/actionpack/README.html.
+
+
+== Getting Started
+
+1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
+ and your application name. Ex: rails myapp
+2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
+3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!"
+4. Follow the guidelines to start developing your application
+
+
+== Web Servers
+
+By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails
+with a variety of other web servers.
+
+Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
+suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
+getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
+More info at: http://mongrel.rubyforge.org
+
+Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or
+Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use
+FCGI or proxy to a pack of Mongrels/Thin/Ebb servers.
+
+== Apache .htaccess example for FCGI/CGI
+
+# General Apache options
+AddHandler fastcgi-script .fcgi
+AddHandler cgi-script .cgi
+Options +FollowSymLinks +ExecCGI
+
+# If you don't want Rails to look in certain directories,
+# use the following rewrite rules so that Apache won't rewrite certain requests
+#
+# Example:
+# RewriteCond %{REQUEST_URI} ^/notrails.*
+# RewriteRule .* - [L]
+
+# Redirect all requests not available on the filesystem to Rails
+# By default the cgi dispatcher is used which is very slow
+#
+# For better performance replace the dispatcher with the fastcgi one
+#
+# Example:
+# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+RewriteEngine On
+
+# If your Rails application is accessed via an Alias directive,
+# then you MUST also set the RewriteBase in this htaccess file.
+#
+# Example:
+# Alias /myrailsapp /path/to/myrailsapp/public
+# RewriteBase /myrailsapp
+
+RewriteRule ^$ index.html [QSA]
+RewriteRule ^([^.]+)$ $1.html [QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
+
+# In case Rails experiences terminal errors
+# Instead of displaying this message you can supply a file here which will be rendered instead
+#
+# Example:
+# ErrorDocument 500 /500.html
+
+ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
+
+
+== Debugging Rails
+
+Sometimes your application goes wrong. Fortunately there are a lot of tools that
+will help you debug it and get it back on the rails.
+
+First area to check is the application log files. Have "tail -f" commands running
+on the server.log and development.log. Rails will automatically display debugging
+and runtime information to these files. Debugging info will also be shown in the
+browser on requests from 127.0.0.1.
+
+You can also log your own messages directly into the log file from your code using
+the Ruby logger class from inside your controllers. Example:
+
+ class WeblogController < ActionController::Base
+ def destroy
+ @weblog = Weblog.find(params[:id])
+ @weblog.destroy
+ logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
+ end
+ end
+
+The result will be a message in your log file along the lines of:
+
+ Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1
+
+More information on how to use the logger is at http://www.ruby-doc.org/core/
+
+Also, Ruby documentation can be found at http://www.ruby-lang.org/ including:
+
+* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/
+* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
+
+These two online (and free) books will bring you up to speed on the Ruby language
+and also on programming in general.
+
+
+== Debugger
+
+Debugger support is available through the debugger command when you start your Mongrel or
+Webrick server with --debugger. This means that you can break out of execution at any point
+in the code, investigate and change the model, AND then resume execution!
+You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'
+Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find(:all)
+ debugger
+ end
+ end
+
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the server window. Here you can do things like:
+
+ >> @posts.inspect
+ => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
+ #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+ >> @posts.first.title = "hello from a debugger"
+ => "hello from a debugger"
+
+...and even better is that you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you enter "cont"
+
+
+== Console
+
+You can interact with the domain model by starting the console through <tt>script/console</tt>.
+Here you'll have all parts of the application configured, just like it is when the
+application is running. You can inspect domain models, change values, and save to the
+database. Starting the script without arguments will launch it in the development environment.
+Passing an argument will specify a different environment, like <tt>script/console production</tt>.
+
+To reload your controllers and models after launching the console run <tt>reload!</tt>
+
+== dbconsole
+
+You can go to the command line of your database directly through <tt>script/dbconsole</tt>.
+You would be connected to the database with the credentials defined in database.yml.
+Starting the script without arguments will connect you to the development database. Passing an
+argument will connect you to a different database, like <tt>script/dbconsole production</tt>.
+Currently works for mysql, postgresql and sqlite.
+
+== Description of Contents
+
+app
+ Holds all the code that's specific to this particular application.
+
+app/controllers
+ Holds controllers that should be named like weblogs_controller.rb for
+ automated URL mapping. All controllers should descend from ApplicationController
+ which itself descends from ActionController::Base.
+
+app/models
+ Holds models that should be named like post.rb.
+ Most models will descend from ActiveRecord::Base.
+
+app/views
+ Holds the template files for the view that should be named like
+ weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby
+ syntax.
+
+app/views/layouts
+ Holds the template files for layouts to be used with views. This models the common
+ header/footer method of wrapping views. In your views, define a layout using the
+ <tt>layout :default</tt> and create a file named default.html.erb. Inside default.html.erb,
+ call <% yield %> to render the view using this layout.
+
+app/helpers
+ Holds view helpers that should be named like weblogs_helper.rb. These are generated
+ for you automatically when using script/generate for controllers. Helpers can be used to
+ wrap functionality for your views into methods.
+
+config
+ Configuration files for the Rails environment, the routing map, the database, and other dependencies.
+
+db
+ Contains the database schema in schema.rb. db/migrate contains all
+ the sequence of Migrations for your schema.
+
+doc
+ This directory is where your application documentation will be stored when generated
+ using <tt>rake doc:app</tt>
+
+lib
+ Application specific libraries. Basically, any kind of custom code that doesn't
+ belong under controllers, models, or helpers. This directory is in the load path.
+
+public
+ The directory available for the web server. Contains subdirectories for images, stylesheets,
+ and javascripts. Also contains the dispatchers and the default HTML files. This should be
+ set as the DOCUMENT_ROOT of your web server.
+
+script
+ Helper scripts for automation and generation.
+
+test
+ Unit and functional tests along with fixtures. When using the script/generate scripts, template
+ test files will be generated for you and placed in this directory.
+
+vendor
+ External libraries that the application depends on. Also includes the plugins subdirectory.
+ If the app has frozen rails, those gems also go here, under vendor/rails/.
+ This directory is in the load path.
diff --git a/railties/fresh_rakefile b/railties/lib/generators/rails/app/templates/Rakefile
index 3bb0e8592a..3bb0e8592a 100755
--- a/railties/fresh_rakefile
+++ b/railties/lib/generators/rails/app/templates/Rakefile
diff --git a/railties/helpers/application_controller.rb b/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb
index 6635a3f487..6635a3f487 100644
--- a/railties/helpers/application_controller.rb
+++ b/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb
diff --git a/railties/helpers/application_helper.rb b/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb
index 22a7940eb2..22a7940eb2 100644
--- a/railties/helpers/application_helper.rb
+++ b/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb
diff --git a/railties/configs/empty.log b/railties/lib/generators/rails/app/templates/app/models/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/configs/empty.log
+++ b/railties/lib/generators/rails/app/templates/app/models/.empty_directory
diff --git a/railties/html/favicon.ico b/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/html/favicon.ico
+++ b/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory
diff --git a/railties/environments/boot.rb b/railties/lib/generators/rails/app/templates/config/boot.rb
index 0ad0f787f8..0ad0f787f8 100644
--- a/railties/environments/boot.rb
+++ b/railties/lib/generators/rails/app/templates/config/boot.rb
diff --git a/railties/configs/databases/frontbase.yml b/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml
index c0c3588be1..c0c3588be1 100644
--- a/railties/configs/databases/frontbase.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml
diff --git a/railties/configs/databases/ibm_db.yml b/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml
index a9716ddb44..a9716ddb44 100644
--- a/railties/configs/databases/ibm_db.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml
diff --git a/railties/configs/databases/mysql.yml b/railties/lib/generators/rails/app/templates/config/databases/mysql.yml
index 1a14bfb332..6bf2f7b1fd 100644
--- a/railties/configs/databases/mysql.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/mysql.yml
@@ -22,8 +22,8 @@ development:
pool: 5
username: root
password:
-<% if socket -%>
- socket: <%= socket %>
+<% if mysql_socket -%>
+ socket: <%= mysql_socket %>
<% else -%>
host: localhost
<% end -%>
@@ -39,8 +39,8 @@ test:
pool: 5
username: root
password:
-<% if socket -%>
- socket: <%= socket %>
+<% if mysql_socket -%>
+ socket: <%= mysql_socket %>
<% else -%>
host: localhost
<% end -%>
@@ -53,8 +53,8 @@ production:
pool: 5
username: root
password:
-<% if socket -%>
- socket: <%= socket %>
+<% if mysql_socket -%>
+ socket: <%= mysql_socket %>
<% else -%>
host: localhost
<% end -%>
diff --git a/railties/configs/databases/oracle.yml b/railties/lib/generators/rails/app/templates/config/databases/oracle.yml
index a1883f6256..a1883f6256 100644
--- a/railties/configs/databases/oracle.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/oracle.yml
diff --git a/railties/configs/databases/postgresql.yml b/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml
index f600e054cf..f600e054cf 100644
--- a/railties/configs/databases/postgresql.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml
diff --git a/railties/configs/databases/sqlite2.yml b/railties/lib/generators/rails/app/templates/config/databases/sqlite2.yml
index 46f01cb42c..46f01cb42c 100644
--- a/railties/configs/databases/sqlite2.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/sqlite2.yml
diff --git a/railties/configs/databases/sqlite3.yml b/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml
index 025d62a8d8..025d62a8d8 100644
--- a/railties/configs/databases/sqlite3.yml
+++ b/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml
diff --git a/railties/environments/environment.rb b/railties/lib/generators/rails/app/templates/config/environment.rb
index 4a2df36307..adb3a3060a 100644
--- a/railties/environments/environment.rb
+++ b/railties/lib/generators/rails/app/templates/config/environment.rb
@@ -1,7 +1,7 @@
# Be sure to restart your server when you modify this file
# Specifies gem version of Rails to use when vendor/rails is not present
-<%= '# ' if freeze %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>' unless defined? RAILS_GEM_VERSION
+<%= '# ' if options[:freeze] %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>' unless defined? RAILS_GEM_VERSION
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
@@ -26,10 +26,14 @@ Rails::Initializer.run do |config|
# Skip frameworks you're not going to use. To use Rails without a database,
# you must remove the Active Record framework.
+<% if options[:skip_activerecord] -%>
+ config.frameworks -= [ :active_record ]
+<% else -%>
# config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
# Activate observers that should always be running
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+<% end -%>
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names.
@@ -38,4 +42,12 @@ Rails::Initializer.run do |config|
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
# config.i18n.default_locale = :de
-end \ No newline at end of file
+
+ # Configure generators values. Many other options are available, be sure to
+ # check the documentation.
+ # config.generators do |g|
+ # g.orm :active_record
+ # g.template_engine :erb
+ # g.test_framework :test_unit, :fixture => true
+ # end
+end
diff --git a/railties/environments/development.rb b/railties/lib/generators/rails/app/templates/config/environments/development.rb
index 85c9a6080e..85c9a6080e 100644
--- a/railties/environments/development.rb
+++ b/railties/lib/generators/rails/app/templates/config/environments/development.rb
diff --git a/railties/environments/production.rb b/railties/lib/generators/rails/app/templates/config/environments/production.rb
index 1fc9f6b923..1fc9f6b923 100644
--- a/railties/environments/production.rb
+++ b/railties/lib/generators/rails/app/templates/config/environments/production.rb
diff --git a/railties/environments/test.rb b/railties/lib/generators/rails/app/templates/config/environments/test.rb
index 496eb9572b..496eb9572b 100644
--- a/railties/environments/test.rb
+++ b/railties/lib/generators/rails/app/templates/config/environments/test.rb
diff --git a/railties/configs/initializers/backtrace_silencers.rb b/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
index 839d4cde19..839d4cde19 100644
--- a/railties/configs/initializers/backtrace_silencers.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
diff --git a/railties/configs/initializers/inflections.rb b/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb
index d531b8bb82..d531b8bb82 100644
--- a/railties/configs/initializers/inflections.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb
diff --git a/railties/configs/initializers/mime_types.rb b/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb
index 72aca7e441..72aca7e441 100644
--- a/railties/configs/initializers/mime_types.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb
diff --git a/railties/configs/initializers/new_rails_defaults.rb b/railties/lib/generators/rails/app/templates/config/initializers/new_rails_defaults.rb
index 8ec3186c84..8ec3186c84 100644
--- a/railties/configs/initializers/new_rails_defaults.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/new_rails_defaults.rb
diff --git a/railties/configs/initializers/session_store.rb b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt
index 4499ab84b6..4499ab84b6 100644
--- a/railties/configs/initializers/session_store.rb
+++ b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt
diff --git a/railties/configs/locales/en.yml b/railties/lib/generators/rails/app/templates/config/locales/en.yml
index f265c068d8..f265c068d8 100644
--- a/railties/configs/locales/en.yml
+++ b/railties/lib/generators/rails/app/templates/config/locales/en.yml
diff --git a/railties/configs/routes.rb b/railties/lib/generators/rails/app/templates/config/routes.rb
index ea14ce1bfc..ea14ce1bfc 100644
--- a/railties/configs/routes.rb
+++ b/railties/lib/generators/rails/app/templates/config/routes.rb
diff --git a/railties/configs/seeds.rb b/railties/lib/generators/rails/app/templates/db/seeds.rb
index 3174d0cb8a..bc8695e6f0 100644
--- a/railties/configs/seeds.rb
+++ b/railties/lib/generators/rails/app/templates/db/seeds.rb
@@ -4,4 +4,4 @@
# Examples:
#
# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
-# Major.create(:name => 'Daley', :city => cities.first)
+# Mayor.create(:name => 'Daley', :city => cities.first)
diff --git a/railties/dispatches/config.ru b/railties/lib/generators/rails/app/templates/dispatchers/config.ru
index acbfe4e9ae..acbfe4e9ae 100644
--- a/railties/dispatches/config.ru
+++ b/railties/lib/generators/rails/app/templates/dispatchers/config.ru
diff --git a/railties/dispatches/dispatch.fcgi b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.fcgi
index 664dbbbee8..f5b3b71875 100755
--- a/railties/dispatches/dispatch.fcgi
+++ b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.fcgi
@@ -1,4 +1,4 @@
-#!/usr/bin/env ruby
+<%= shebang %>
#
# You may specify the path to the FastCGI crash log (a log of unhandled
# exceptions which forced the FastCGI instance to exit, great for debugging)
diff --git a/railties/dispatches/dispatch.rb b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.rb
index 32fa3b2665..48e888113a 100755
--- a/railties/dispatches/dispatch.rb
+++ b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.rb
@@ -1,4 +1,4 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
diff --git a/railties/dispatches/gateway.cgi b/railties/lib/generators/rails/app/templates/dispatchers/gateway.cgi
index 0305b7f810..bdc1055a22 100755
--- a/railties/dispatches/gateway.cgi
+++ b/railties/lib/generators/rails/app/templates/dispatchers/gateway.cgi
@@ -1,4 +1,4 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require 'drb'
diff --git a/railties/doc/README_FOR_APP b/railties/lib/generators/rails/app/templates/doc/README_FOR_APP
index fe41f5cc24..fe41f5cc24 100644
--- a/railties/doc/README_FOR_APP
+++ b/railties/lib/generators/rails/app/templates/doc/README_FOR_APP
diff --git a/railties/html/404.html b/railties/lib/generators/rails/app/templates/public/404.html
index 88ee108e90..88ee108e90 100644
--- a/railties/html/404.html
+++ b/railties/lib/generators/rails/app/templates/public/404.html
diff --git a/railties/html/422.html b/railties/lib/generators/rails/app/templates/public/422.html
index 9c3c96670b..9c3c96670b 100644
--- a/railties/html/422.html
+++ b/railties/lib/generators/rails/app/templates/public/422.html
diff --git a/railties/html/500.html b/railties/lib/generators/rails/app/templates/public/500.html
index f71c86e652..f71c86e652 100644
--- a/railties/html/500.html
+++ b/railties/lib/generators/rails/app/templates/public/500.html
diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml b/railties/lib/generators/rails/app/templates/public/favicon.ico
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml
+++ b/railties/lib/generators/rails/app/templates/public/favicon.ico
diff --git a/railties/html/images/rails.png b/railties/lib/generators/rails/app/templates/public/images/rails.png
index d5edc04e65..d5edc04e65 100644
--- a/railties/html/images/rails.png
+++ b/railties/lib/generators/rails/app/templates/public/images/rails.png
Binary files differ
diff --git a/railties/html/index.html b/railties/lib/generators/rails/app/templates/public/index.html
index ff2dfd3193..ff2dfd3193 100644
--- a/railties/html/index.html
+++ b/railties/lib/generators/rails/app/templates/public/index.html
diff --git a/railties/html/javascripts/application.js b/railties/lib/generators/rails/app/templates/public/javascripts/application.js
index fe4577696b..fe4577696b 100644
--- a/railties/html/javascripts/application.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/application.js
diff --git a/railties/html/javascripts/controls.js b/railties/lib/generators/rails/app/templates/public/javascripts/controls.js
index ca29aefdd1..ca29aefdd1 100644
--- a/railties/html/javascripts/controls.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/controls.js
diff --git a/railties/html/javascripts/dragdrop.js b/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js
index 07229f986f..07229f986f 100644
--- a/railties/html/javascripts/dragdrop.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js
diff --git a/railties/html/javascripts/effects.js b/railties/lib/generators/rails/app/templates/public/javascripts/effects.js
index 5a639d2dea..5a639d2dea 100644
--- a/railties/html/javascripts/effects.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/effects.js
diff --git a/railties/html/javascripts/prototype.js b/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js
index dfe8ab4e13..dfe8ab4e13 100644
--- a/railties/html/javascripts/prototype.js
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js
diff --git a/railties/html/robots.txt b/railties/lib/generators/rails/app/templates/public/robots.txt
index 085187fa58..085187fa58 100644
--- a/railties/html/robots.txt
+++ b/railties/lib/generators/rails/app/templates/public/robots.txt
diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml b/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml
+++ b/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory
diff --git a/railties/bin/about b/railties/lib/generators/rails/app/templates/script/about.tt
index 1eeb6eb915..afbab8b533 100755
--- a/railties/bin/about
+++ b/railties/lib/generators/rails/app/templates/script/about.tt
@@ -1,4 +1,4 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
$LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info"
require 'commands/about'
diff --git a/railties/bin/console b/railties/lib/generators/rails/app/templates/script/console.tt
index 235a1f2780..2f8b95c923 100755
--- a/railties/bin/console
+++ b/railties/lib/generators/rails/app/templates/script/console.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
require 'commands/console'
diff --git a/railties/bin/dbconsole b/railties/lib/generators/rails/app/templates/script/dbconsole.tt
index 83c8436a9d..7dce6d16dd 100755
--- a/railties/bin/dbconsole
+++ b/railties/lib/generators/rails/app/templates/script/dbconsole.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
require 'commands/dbconsole'
diff --git a/railties/bin/destroy b/railties/lib/generators/rails/app/templates/script/destroy.tt
index 88d295f7aa..053d3dea39 100755
--- a/railties/bin/destroy
+++ b/railties/lib/generators/rails/app/templates/script/destroy.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
require 'commands/destroy'
diff --git a/railties/bin/generate b/railties/lib/generators/rails/app/templates/script/generate.tt
index 62a8a4c0c5..ea4a0efd43 100755
--- a/railties/bin/generate
+++ b/railties/lib/generators/rails/app/templates/script/generate.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
require 'commands/generate'
diff --git a/railties/bin/performance/benchmarker b/railties/lib/generators/rails/app/templates/script/performance/benchmarker.tt
index 3bff809fb3..da9df7fab9 100755
--- a/railties/bin/performance/benchmarker
+++ b/railties/lib/generators/rails/app/templates/script/performance/benchmarker.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../../config/boot', __FILE__)
require 'commands/performance/benchmarker'
diff --git a/railties/bin/performance/profiler b/railties/lib/generators/rails/app/templates/script/performance/profiler.tt
index 07640575cd..5a0e2b0c28 100755
--- a/railties/bin/performance/profiler
+++ b/railties/lib/generators/rails/app/templates/script/performance/profiler.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../../config/boot', __FILE__)
require 'commands/performance/profiler'
diff --git a/railties/bin/plugin b/railties/lib/generators/rails/app/templates/script/plugin.tt
index b82201fa83..68b2148bba 100755
--- a/railties/bin/plugin
+++ b/railties/lib/generators/rails/app/templates/script/plugin.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
require 'commands/plugin'
diff --git a/railties/bin/runner b/railties/lib/generators/rails/app/templates/script/runner.tt
index be4c5d4572..a2b313fa32 100755
--- a/railties/bin/runner
+++ b/railties/lib/generators/rails/app/templates/script/runner.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
require 'commands/runner'
diff --git a/railties/bin/server b/railties/lib/generators/rails/app/templates/script/server.tt
index b9fcb71793..c8868155f3 100755
--- a/railties/bin/server
+++ b/railties/lib/generators/rails/app/templates/script/server.tt
@@ -1,3 +1,3 @@
-#!/usr/bin/env ruby
+<%= shebang %>
require File.expand_path('../../config/boot', __FILE__)
require 'commands/server'
diff --git a/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb b/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb
+++ b/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory
diff --git a/railties/test/fixtures/lib/generators/missing_class/templates/.gitignore b/railties/lib/generators/rails/app/templates/test/functional/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/test/fixtures/lib/generators/missing_class/templates/.gitignore
+++ b/railties/lib/generators/rails/app/templates/test/functional/.empty_directory
diff --git a/railties/test/fixtures/lib/generators/missing_generator/templates/.gitignore b/railties/lib/generators/rails/app/templates/test/integration/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/test/fixtures/lib/generators/missing_generator/templates/.gitignore
+++ b/railties/lib/generators/rails/app/templates/test/integration/.empty_directory
diff --git a/railties/helpers/performance_test.rb b/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb
index 4b60558b43..4b60558b43 100644
--- a/railties/helpers/performance_test.rb
+++ b/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb
diff --git a/railties/helpers/test_helper.rb b/railties/lib/generators/rails/app/templates/test/test_helper.rb
index b9fe2517c8..b9fe2517c8 100644
--- a/railties/helpers/test_helper.rb
+++ b/railties/lib/generators/rails/app/templates/test/test_helper.rb
diff --git a/railties/test/fixtures/lib/generators/missing_templates/.gitignore b/railties/lib/generators/rails/app/templates/test/unit/.empty_directory
index e69de29bb2..e69de29bb2 100644
--- a/railties/test/fixtures/lib/generators/missing_templates/.gitignore
+++ b/railties/lib/generators/rails/app/templates/test/unit/.empty_directory
diff --git a/railties/lib/generators/rails/controller/USAGE b/railties/lib/generators/rails/controller/USAGE
new file mode 100644
index 0000000000..6ed4b2edfc
--- /dev/null
+++ b/railties/lib/generators/rails/controller/USAGE
@@ -0,0 +1,18 @@
+Description:
+ Stubs out a new controller and its views. Pass the controller name, either
+ CamelCased or under_scored, and a list of views as arguments.
+
+ To create a controller within a module, specify the controller name as a
+ path like 'parent_module/controller_name'.
+
+ This generates a controller class in app/controllers and invokes helper,
+ template engine and test framework generators.
+
+Example:
+ `./script/generate controller CreditCard open debit credit close`
+
+ Credit card controller with URLs like /credit_card/debit.
+ Controller: app/controllers/credit_card_controller.rb
+ Functional Test: test/functional/credit_card_controller_test.rb
+ Views: app/views/credit_card/debit.html.erb [...]
+ Helper: app/helpers/credit_card_helper.rb
diff --git a/railties/lib/generators/rails/controller/controller_generator.rb b/railties/lib/generators/rails/controller/controller_generator.rb
new file mode 100644
index 0000000000..91470be833
--- /dev/null
+++ b/railties/lib/generators/rails/controller/controller_generator.rb
@@ -0,0 +1,14 @@
+module Rails
+ module Generators
+ class ControllerGenerator < NamedBase
+ argument :actions, :type => :array, :default => [], :banner => "action action"
+ check_class_collision :suffix => "Controller"
+
+ def create_controller_files
+ template 'controller.rb', File.join('app/controllers', class_path, "#{file_name}_controller.rb")
+ end
+
+ hook_for :template_engine, :test_framework, :helper
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/controller/templates/controller.rb b/railties/lib/generators/rails/controller/templates/controller.rb
index cda2659e69..cda2659e69 100644
--- a/railties/lib/rails_generator/generators/components/controller/templates/controller.rb
+++ b/railties/lib/generators/rails/controller/templates/controller.rb
diff --git a/railties/lib/generators/rails/generator/USAGE b/railties/lib/generators/rails/generator/USAGE
new file mode 100644
index 0000000000..ca7d3f62d0
--- /dev/null
+++ b/railties/lib/generators/rails/generator/USAGE
@@ -0,0 +1,11 @@
+Description:
+ Stubs out a new generator at lib/generators. Pass the generator name, either
+ CamelCased or under_scored, as an argument.
+
+Example:
+ `./script/generate generator Awesome`
+
+ creates a standard awesome generator:
+ lib/generators/awesome/
+ lib/generators/awesome/awesome_generator.rb
+ lib/generators/awesome/templates/
diff --git a/railties/lib/generators/rails/generator/generator_generator.rb b/railties/lib/generators/rails/generator/generator_generator.rb
new file mode 100644
index 0000000000..2fc97b20d3
--- /dev/null
+++ b/railties/lib/generators/rails/generator/generator_generator.rb
@@ -0,0 +1,25 @@
+module Rails
+ module Generators
+ class GeneratorGenerator < NamedBase
+ check_class_collision :suffix => "Generator"
+
+ class_option :namespace, :type => :boolean, :default => true,
+ :desc => "Namespace generator under lib/generators/name"
+
+ def craete_generator_files
+ directory '.', generator_dir
+ end
+
+ protected
+
+ def generator_dir
+ if options[:namespace]
+ File.join("lib", "generators", file_name)
+ else
+ File.join("lib", "generators")
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt
new file mode 100644
index 0000000000..675f00043f
--- /dev/null
+++ b/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt
@@ -0,0 +1,5 @@
+class <%= class_name %>Generator < Rails::Generators::NamedBase
+ def self.source_root
+ @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/USAGE b/railties/lib/generators/rails/generator/templates/USAGE.tt
index ea9f4f12cc..ea9f4f12cc 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/USAGE
+++ b/railties/lib/generators/rails/generator/templates/USAGE.tt
diff --git a/railties/lib/generators/rails/generator/templates/templates/.empty_directory b/railties/lib/generators/rails/generator/templates/templates/.empty_directory
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/lib/generators/rails/generator/templates/templates/.empty_directory
diff --git a/railties/lib/generators/rails/helper/USAGE b/railties/lib/generators/rails/helper/USAGE
new file mode 100644
index 0000000000..531c9b390a
--- /dev/null
+++ b/railties/lib/generators/rails/helper/USAGE
@@ -0,0 +1,17 @@
+Description:
+ Stubs out a new helper. Pass the helper name, either CamelCased
+ or under_scored.
+
+ To create a helper within a module, specify the helper name as a
+ path like 'parent_module/helper_name'.
+
+ This generates a helper class in app/helpers and invokes the configured
+ test framework.
+
+Example:
+ `./script/generate helper CreditCard`
+
+ Credit card helper.
+ Helper: app/helpers/credit_card_helper.rb
+ Test: test/unit/helpers/credit_card_helper_test.rb
+
diff --git a/railties/lib/generators/rails/helper/helper_generator.rb b/railties/lib/generators/rails/helper/helper_generator.rb
new file mode 100644
index 0000000000..ad66388591
--- /dev/null
+++ b/railties/lib/generators/rails/helper/helper_generator.rb
@@ -0,0 +1,13 @@
+module Rails
+ module Generators
+ class HelperGenerator < NamedBase
+ check_class_collision :suffix => "Helper"
+
+ def create_helper_files
+ template 'helper.rb', File.join('app/helpers', class_path, "#{file_name}_helper.rb")
+ end
+
+ hook_for :test_framework
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/controller/templates/helper.rb b/railties/lib/generators/rails/helper/templates/helper.rb
index 3fe2ecdc74..3fe2ecdc74 100644
--- a/railties/lib/rails_generator/generators/components/controller/templates/helper.rb
+++ b/railties/lib/generators/rails/helper/templates/helper.rb
diff --git a/railties/lib/rails_generator/generators/components/integration_test/USAGE b/railties/lib/generators/rails/integration_test/USAGE
index 09e2691f69..b76c35a702 100644
--- a/railties/lib/rails_generator/generators/components/integration_test/USAGE
+++ b/railties/lib/generators/rails/integration_test/USAGE
@@ -1,7 +1,9 @@
Description:
Stubs out a new integration test. Pass the name of the test, either
- CamelCased or under_scored, as an argument. The new test class is
- generated in test/integration/testname_test.rb
+ CamelCased or under_scored, as an argument.
+
+ This generator invokes the current integration tool, which defaults to
+ TestUnit.
Example:
`./script/generate integration_test GeneralStories` creates a GeneralStories
diff --git a/railties/lib/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/generators/rails/integration_test/integration_test_generator.rb
new file mode 100644
index 0000000000..363a327fcb
--- /dev/null
+++ b/railties/lib/generators/rails/integration_test/integration_test_generator.rb
@@ -0,0 +1,7 @@
+module Rails
+ module Generators
+ class IntegrationTestGenerator < NamedBase
+ hook_for :integration_tool, :as => :integration
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/mailer/USAGE b/railties/lib/generators/rails/mailer/USAGE
index 61a649ed4d..c56095b2c8 100644
--- a/railties/lib/rails_generator/generators/components/mailer/USAGE
+++ b/railties/lib/generators/rails/mailer/USAGE
@@ -2,9 +2,8 @@ Description:
Stubs out a new mailer and its views. Pass the mailer name, either
CamelCased or under_scored, and an optional list of emails as arguments.
- This generates a mailer class in app/models, view templates in
- app/views/mailer_name, a unit test in test/unit, and fixtures in
- test/fixtures.
+ This generates a mailer class in app/models and invokes your template
+ engine and test framework generators.
Example:
`./script/generate mailer Notifications signup forgot_password invoice`
diff --git a/railties/lib/generators/rails/mailer/mailer_generator.rb b/railties/lib/generators/rails/mailer/mailer_generator.rb
new file mode 100644
index 0000000000..33f1665b83
--- /dev/null
+++ b/railties/lib/generators/rails/mailer/mailer_generator.rb
@@ -0,0 +1,14 @@
+module Rails
+ module Generators
+ class MailerGenerator < NamedBase
+ argument :actions, :type => :array, :default => [], :banner => "method method"
+ check_class_collision
+
+ def create_mailer_file
+ template "mailer.rb", File.join('app/models', class_path, "#{file_name}.rb")
+ end
+
+ hook_for :template_engine, :test_framework
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb b/railties/lib/generators/rails/mailer/templates/mailer.rb
index ce15ae9de9..90e0b712d6 100644
--- a/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb
+++ b/railties/lib/generators/rails/mailer/templates/mailer.rb
@@ -1,5 +1,4 @@
class <%= class_name %> < ActionMailer::Base
-
<% for action in actions -%>
def <%= action %>(sent_at = Time.now)
@@ -10,6 +9,6 @@ class <%= class_name %> < ActionMailer::Base
body :greeting => 'Hi,'
end
-<% end -%>
+<% end -%>
end
diff --git a/railties/lib/rails_generator/generators/components/metal/USAGE b/railties/lib/generators/rails/metal/USAGE
index 123ec6c03f..123ec6c03f 100644
--- a/railties/lib/rails_generator/generators/components/metal/USAGE
+++ b/railties/lib/generators/rails/metal/USAGE
diff --git a/railties/lib/generators/rails/metal/metal_generator.rb b/railties/lib/generators/rails/metal/metal_generator.rb
new file mode 100644
index 0000000000..fe4f945cad
--- /dev/null
+++ b/railties/lib/generators/rails/metal/metal_generator.rb
@@ -0,0 +1,11 @@
+module Rails
+ module Generators
+ class MetalGenerator < NamedBase
+ check_class_collision
+
+ def create_metal_file
+ template "metal.rb", "app/metal/#{file_name}.rb"
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/metal/templates/metal.rb b/railties/lib/generators/rails/metal/templates/metal.rb
index e94982b69a..e94982b69a 100644
--- a/railties/lib/rails_generator/generators/components/metal/templates/metal.rb
+++ b/railties/lib/generators/rails/metal/templates/metal.rb
diff --git a/railties/lib/rails_generator/generators/components/migration/USAGE b/railties/lib/generators/rails/migration/USAGE
index b83c657963..d91127aac3 100644
--- a/railties/lib/rails_generator/generators/components/migration/USAGE
+++ b/railties/lib/generators/rails/migration/USAGE
@@ -14,7 +14,7 @@ Example:
db/migrate/20080514090912_add_ssl_flag.rb
`./script/generate migration AddTitleBodyToPost title:string body:text published:boolean`
-
+
This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with
this in the Up migration:
@@ -23,7 +23,7 @@ Example:
add_column :posts, :published, :boolean
And this in the Down migration:
-
+
remove_column :posts, :published
remove_column :posts, :body
remove_column :posts, :title
diff --git a/railties/lib/generators/rails/migration/migration_generator.rb b/railties/lib/generators/rails/migration/migration_generator.rb
new file mode 100644
index 0000000000..39fa5b63b1
--- /dev/null
+++ b/railties/lib/generators/rails/migration/migration_generator.rb
@@ -0,0 +1,8 @@
+module Rails
+ module Generators
+ class MigrationGenerator < NamedBase #metagenerator
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ hook_for :orm, :required => true
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/model/USAGE b/railties/lib/generators/rails/model/USAGE
index 24b03b4d4a..b056d5df8b 100644
--- a/railties/lib/rails_generator/generators/components/model/USAGE
+++ b/railties/lib/generators/rails/model/USAGE
@@ -2,21 +2,24 @@ Description:
Stubs out a new model. Pass the model name, either CamelCased or
under_scored, and an optional list of attribute pairs as arguments.
- Attribute pairs are column_name:sql_type arguments specifying the
+ Attribute pairs are field:type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
specify them by hand as 'created_at:datetime updated_at:datetime'.
You don't have to think up every attribute up front, but it helps to
sketch out a few so you can start working with the model immediately.
- This generates a model class in app/models, a unit test in test/unit,
- a test fixture in test/fixtures/singular_name.yml, and a migration in
- db/migrate.
+ This generator invokes your configured ORM and test framework, which
+ defaults to ActiveRecord and TestUnit.
+
+ Finally, if --parent option is given, it's used as superclass of the
+ created model. This allows you create Single Table Inheritance models.
Examples:
`./script/generate model account`
- creates an Account model, test, fixture, and migration:
+ For ActiveRecord and TestUnit it creates:
+
Model: app/models/account.rb
Test: test/unit/account_test.rb
Fixtures: test/fixtures/accounts.yml
@@ -24,4 +27,4 @@ Examples:
`./script/generate model post title:string body:text published:boolean`
- creates a Post model with a string title, text body, and published flag.
+ Creates a Post model with a string title, text body, and published flag.
diff --git a/railties/lib/generators/rails/model/model_generator.rb b/railties/lib/generators/rails/model/model_generator.rb
new file mode 100644
index 0000000000..629d5eed3f
--- /dev/null
+++ b/railties/lib/generators/rails/model/model_generator.rb
@@ -0,0 +1,8 @@
+module Rails
+ module Generators
+ class ModelGenerator < NamedBase #metagenerator
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ hook_for :orm, :required => true
+ end
+ end
+end
diff --git a/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb b/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb
new file mode 100644
index 0000000000..4649709780
--- /dev/null
+++ b/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb
@@ -0,0 +1,11 @@
+module Rails
+ module Generators
+ class ModelSubclassGenerator < Base
+ desc "model_subclass is deprecated. Invoke model with --parent option instead."
+
+ def say_deprecation_warn
+ say self.class.desc
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/observer/USAGE b/railties/lib/generators/rails/observer/USAGE
index a5d744a3c2..9a20f55a89 100644
--- a/railties/lib/rails_generator/generators/components/observer/USAGE
+++ b/railties/lib/generators/rails/observer/USAGE
@@ -2,12 +2,11 @@ Description:
Stubs out a new observer. Pass the observer name, either CamelCased or
under_scored, as an argument.
- The generator creates an observer class in app/models and a unit test in
- test/unit.
+ This generator only invokes your ORM and test framework generators.
Example:
`./script/generate observer Account`
- creates an Account observer and unit test:
+ For ActiveRecord and TestUnit it creates:
Observer: app/models/account_observer.rb
- Test: test/unit/account_observer_test.rb
+ TestUnit: test/unit/account_observer_test.rb
diff --git a/railties/lib/generators/rails/observer/observer_generator.rb b/railties/lib/generators/rails/observer/observer_generator.rb
new file mode 100644
index 0000000000..f5cedee91f
--- /dev/null
+++ b/railties/lib/generators/rails/observer/observer_generator.rb
@@ -0,0 +1,7 @@
+module Rails
+ module Generators
+ class ObserverGenerator < NamedBase #metagenerator
+ hook_for :orm, :required => true
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/performance_test/USAGE b/railties/lib/generators/rails/performance_test/USAGE
index d84051eb02..ee82578cdb 100644
--- a/railties/lib/rails_generator/generators/components/performance_test/USAGE
+++ b/railties/lib/generators/rails/performance_test/USAGE
@@ -1,7 +1,9 @@
Description:
Stubs out a new performance test. Pass the name of the test, either
- CamelCased or under_scored, as an argument. The new test class is
- generated in test/performance/testname_test.rb
+ CamelCased or under_scored, as an argument.
+
+ This generator invokes the current performance tool, which defaults to
+ TestUnit.
Example:
`./script/generate performance_test GeneralStories` creates a GeneralStories
diff --git a/railties/lib/generators/rails/performance_test/performance_test_generator.rb b/railties/lib/generators/rails/performance_test/performance_test_generator.rb
new file mode 100644
index 0000000000..d1c71ab8ed
--- /dev/null
+++ b/railties/lib/generators/rails/performance_test/performance_test_generator.rb
@@ -0,0 +1,7 @@
+module Rails
+ module Generators
+ class PerformanceTestGenerator < NamedBase
+ hook_for :performance_tool, :as => :performance
+ end
+ end
+end
diff --git a/railties/lib/generators/rails/plugin/USAGE b/railties/lib/generators/rails/plugin/USAGE
new file mode 100644
index 0000000000..8a17fa4dec
--- /dev/null
+++ b/railties/lib/generators/rails/plugin/USAGE
@@ -0,0 +1,13 @@
+Description:
+ Stubs out a new plugin at vendor/plugins. Pass the plugin name, either
+ CamelCased or under_scored, as an argument.
+
+Example:
+ `./script/generate plugin BrowserFilters`
+
+ creates a standard browser_filters plugin:
+ vendor/plugins/browser_filters/README
+ vendor/plugins/browser_filters/init.rb
+ vendor/plugins/browser_filters/install.rb
+ vendor/plugins/browser_filters/lib/browser_filters.rb
+ vendor/plugins/browser_filters/test/browser_filters_test.rb
diff --git a/railties/lib/generators/rails/plugin/plugin_generator.rb b/railties/lib/generators/rails/plugin/plugin_generator.rb
new file mode 100644
index 0000000000..ee785caf7d
--- /dev/null
+++ b/railties/lib/generators/rails/plugin/plugin_generator.rb
@@ -0,0 +1,47 @@
+require 'generators/rails/generator/generator_generator'
+
+module Rails
+ module Generators
+ class PluginGenerator < NamedBase
+ class_option :tasks, :desc => "When supplied creates tasks base files."
+
+ check_class_collision
+
+ def create_root_files
+ directory '.', plugin_dir, :recursive => false
+ end
+
+ def create_lib_files
+ directory 'lib', plugin_dir('lib'), :recursive => false
+ end
+
+ def create_tasks_files
+ return unless options[:tasks]
+ 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
+ end
+ end
+
+ hook_for :test_framework do |instance, test_framework|
+ instance.inside instance.send(:plugin_dir), :verbose => true do
+ instance.invoke test_framework
+ end
+ end
+
+ protected
+
+ def plugin_dir(join=nil)
+ if join
+ File.join(plugin_dir, join)
+ else
+ "vendor/plugins/#{file_name}"
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE b/railties/lib/generators/rails/plugin/templates/MIT-LICENSE
index 8717df053d..8717df053d 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE
+++ b/railties/lib/generators/rails/plugin/templates/MIT-LICENSE
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/README b/railties/lib/generators/rails/plugin/templates/README
index 702db07cb1..702db07cb1 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/README
+++ b/railties/lib/generators/rails/plugin/templates/README
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile b/railties/lib/generators/rails/plugin/templates/Rakefile
index 85e8ff1834..85e8ff1834 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile
+++ b/railties/lib/generators/rails/plugin/templates/Rakefile
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/init.rb b/railties/lib/generators/rails/plugin/templates/init.rb
index 3c19a743c9..3c19a743c9 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/init.rb
+++ b/railties/lib/generators/rails/plugin/templates/init.rb
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/install.rb b/railties/lib/generators/rails/plugin/templates/install.rb
index f7732d3796..f7732d3796 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/install.rb
+++ b/railties/lib/generators/rails/plugin/templates/install.rb
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb b/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt
index d8d908a959..d8d908a959 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb
+++ b/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake b/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt
index 72920a9d3a..72920a9d3a 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake
+++ b/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb b/railties/lib/generators/rails/plugin/templates/uninstall.rb
index 9738333463..9738333463 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb
+++ b/railties/lib/generators/rails/plugin/templates/uninstall.rb
diff --git a/railties/lib/rails_generator/generators/components/resource/USAGE b/railties/lib/generators/rails/resource/USAGE
index e6043f1de1..936619b0db 100644
--- a/railties/lib/rails_generator/generators/components/resource/USAGE
+++ b/railties/lib/generators/rails/resource/USAGE
@@ -4,15 +4,15 @@ Description:
either CamelCased or under_scored, as the first argument, and an optional
list of attribute pairs.
- Attribute pairs are column_name:sql_type arguments specifying the
+ Attribute pairs are field:type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
specify them by hand as 'created_at:datetime updated_at:datetime'.
You don't have to think up every attribute up front, but it helps to
- sketch out a few so you can start working with the resource immediately.
+ sketch out a few so you can start working with the model immediately.
- This creates a model, controller, helper, tests and fixtures for all of them,
- and the corresponding map.resources declaration in config/routes.rb
+ This generator invokes your configured ORM and test framework, besides
+ creating helpers and add routes to config/routes.rb.
Unlike the scaffold generator, the resource generator does not create
views or add any methods to the generated controller.
diff --git a/railties/lib/generators/rails/resource/resource_generator.rb b/railties/lib/generators/rails/resource/resource_generator.rb
new file mode 100644
index 0000000000..70babc0550
--- /dev/null
+++ b/railties/lib/generators/rails/resource/resource_generator.rb
@@ -0,0 +1,40 @@
+require 'generators/rails/model/model_generator'
+
+module Rails
+ module Generators
+ class ResourceGenerator < ModelGenerator #metagenerator
+ hook_for :resource_controller, :required => true do |base, controller|
+ base.invoke controller, [ base.name.pluralize, base.options[:actions] ]
+ end
+
+ class_option :actions, :type => :array, :banner => "ACTION ACTION", :default => [],
+ :desc => "Actions for the resource controller"
+
+ class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller"
+ class_option :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName"
+
+ def initialize(*args)
+ super
+ if name == name.pluralize && !options[:force_plural]
+ say "Plural version of the model detected, using singularized version. Override with --force-plural."
+ name.replace name.singularize
+ end
+ end
+
+ def add_resource_route
+ route "map.resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}"
+ end
+
+ protected
+
+ def pluralize?(name)
+ if options[:singleton]
+ name
+ else
+ name.pluralize
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/USAGE b/railties/lib/generators/rails/scaffold/USAGE
index 810aea16f1..71edd2f469 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/USAGE
+++ b/railties/lib/generators/rails/scaffold/USAGE
@@ -7,7 +7,7 @@ Description:
under_scored, as the first argument, and an optional list of attribute
pairs.
- Attribute pairs are column_name:sql_type arguments specifying the
+ Attribute pairs are field:type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
specify them by hand as 'created_at:datetime updated_at:datetime'.
diff --git a/railties/lib/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/generators/rails/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..af44c8ba65
--- /dev/null
+++ b/railties/lib/generators/rails/scaffold/scaffold_generator.rb
@@ -0,0 +1,12 @@
+require 'generators/rails/resource/resource_generator'
+
+module Rails
+ module Generators
+ class ScaffoldGenerator < ResourceGenerator #metagenerator
+ remove_hook_for :actions, :resource_controller
+
+ hook_for :scaffold_controller, :required => true
+ hook_for :stylesheets
+ end
+ end
+end
diff --git a/railties/lib/generators/rails/scaffold_controller/USAGE b/railties/lib/generators/rails/scaffold_controller/USAGE
new file mode 100644
index 0000000000..d60a3c3680
--- /dev/null
+++ b/railties/lib/generators/rails/scaffold_controller/USAGE
@@ -0,0 +1,20 @@
+Description:
+ Stubs out a scaffolded controller and its views. Pass the model name,
+ either CamelCased or under_scored, and a list of views as arguments.
+ The controller name is retrieved as a pluralized version of the model
+ name.
+
+ To create a controller within a module, specify the model name as a
+ path like 'parent_module/controller_name'.
+
+ This generates a controller class in app/controllers and invokes helper,
+ template engine and test framework generators.
+
+Example:
+ `./script/generate scaffold_controller CreditCard`
+
+ Credit card controller with URLs like /credit_card/debit.
+ Controller: app/controllers/credit_cards_controller.rb
+ Functional Test: test/functional/credit_cards_controller_test.rb
+ Views: app/views/credit_cards/index.html.erb [...]
+ Helper: app/helpers/credit_cards_helper.rb
diff --git a/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb
new file mode 100644
index 0000000000..972be5a33b
--- /dev/null
+++ b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb
@@ -0,0 +1,26 @@
+module Rails
+ module Generators
+ class ScaffoldControllerGenerator < NamedBase
+ # Add controller methods and ActionORM settings.
+ include ScaffoldBase
+
+ check_class_collision :suffix => "Controller"
+
+ class_option :orm, :banner => "NAME", :type => :string, :required => true,
+ :desc => "ORM to generate the controller for"
+
+ class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller"
+
+ def create_controller_files
+ template 'controller.rb', File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb")
+ end
+
+ hook_for :template_engine, :test_framework, :as => :scaffold
+
+ # Invoke the helper using the controller (pluralized) name.
+ hook_for :helper, :as => :scaffold do |base, invoked|
+ base.invoke invoked, [ base.controller_name ]
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb b/railties/lib/generators/rails/scaffold_controller/templates/controller.rb
index 4d190b9362..3cc8bbf8e7 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb
+++ b/railties/lib/generators/rails/scaffold_controller/templates/controller.rb
@@ -1,19 +1,21 @@
class <%= controller_class_name %>Controller < ApplicationController
+<% unless options[:singleton] -%>
# GET /<%= table_name %>
# GET /<%= table_name %>.xml
def index
- @<%= table_name %> = <%= class_name %>.all
+ @<%= table_name %> = <%= orm_class.all(class_name) %>
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @<%= table_name %> }
end
end
+<% end -%>
# GET /<%= table_name %>/1
# GET /<%= table_name %>/1.xml
def show
- @<%= file_name %> = <%= class_name %>.find(params[:id])
+ @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
respond_to do |format|
format.html # show.html.erb
@@ -24,7 +26,7 @@ class <%= controller_class_name %>Controller < ApplicationController
# GET /<%= table_name %>/new
# GET /<%= table_name %>/new.xml
def new
- @<%= file_name %> = <%= class_name %>.new
+ @<%= file_name %> = <%= orm_class.build(class_name) %>
respond_to do |format|
format.html # new.html.erb
@@ -34,22 +36,22 @@ class <%= controller_class_name %>Controller < ApplicationController
# GET /<%= table_name %>/1/edit
def edit
- @<%= file_name %> = <%= class_name %>.find(params[:id])
+ @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
end
# POST /<%= table_name %>
# POST /<%= table_name %>.xml
def create
- @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
+ @<%= file_name %> = <%= orm_class.build(class_name, "params[:#{file_name}]") %>
respond_to do |format|
- if @<%= file_name %>.save
+ if @<%= orm_instance.save %>
flash[:notice] = '<%= class_name %> was successfully created.'
format.html { redirect_to(@<%= file_name %>) }
format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
else
format.html { render :action => "new" }
- format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
+ format.xml { render :xml => @<%= orm_instance.errors %>, :status => :unprocessable_entity }
end
end
end
@@ -57,16 +59,16 @@ class <%= controller_class_name %>Controller < ApplicationController
# PUT /<%= table_name %>/1
# PUT /<%= table_name %>/1.xml
def update
- @<%= file_name %> = <%= class_name %>.find(params[:id])
+ @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
respond_to do |format|
- if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
+ if @<%= orm_instance.update_attributes("params[:#{file_name}]") %>
flash[:notice] = '<%= class_name %> was successfully updated.'
format.html { redirect_to(@<%= file_name %>) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
- format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
+ format.xml { render :xml => @<%= orm_instance.errors %>, :status => :unprocessable_entity }
end
end
end
@@ -74,8 +76,8 @@ class <%= controller_class_name %>Controller < ApplicationController
# DELETE /<%= table_name %>/1
# DELETE /<%= table_name %>/1.xml
def destroy
- @<%= file_name %> = <%= class_name %>.find(params[:id])
- @<%= file_name %>.destroy
+ @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
+ @<%= orm_instance.destroy %>
respond_to do |format|
format.html { redirect_to(<%= table_name %>_url) }
diff --git a/railties/lib/generators/rails/session_migration/USAGE b/railties/lib/generators/rails/session_migration/USAGE
new file mode 100644
index 0000000000..e106f6ecc8
--- /dev/null
+++ b/railties/lib/generators/rails/session_migration/USAGE
@@ -0,0 +1,8 @@
+Description:
+ Creates a migration to add the sessions table used by the ORM session store.
+ Pass the migration name, either CamelCased or under_scored, as an argument.
+
+ Before invoking this generator, be sure that your ORM supports session stores.
+
+Example:
+ `./script/generate session_migration CreateSessionTable`
diff --git a/railties/lib/generators/rails/session_migration/session_migration_generator.rb b/railties/lib/generators/rails/session_migration/session_migration_generator.rb
new file mode 100644
index 0000000000..258cc5b4a0
--- /dev/null
+++ b/railties/lib/generators/rails/session_migration/session_migration_generator.rb
@@ -0,0 +1,8 @@
+module Rails
+ module Generators
+ class SessionMigrationGenerator < NamedBase #metagenerator
+ argument :name, :type => :string, :default => "add_sessions_table"
+ hook_for :orm, :required => true
+ end
+ end
+end
diff --git a/railties/lib/generators/rails/stylesheets/USAGE b/railties/lib/generators/rails/stylesheets/USAGE
new file mode 100644
index 0000000000..d6a81e51d0
--- /dev/null
+++ b/railties/lib/generators/rails/stylesheets/USAGE
@@ -0,0 +1,5 @@
+Description:
+ Copies scaffold stylesheets to public/stylesheets/.
+
+Examples:
+ `./script/generate stylesheets`
diff --git a/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb b/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb
new file mode 100644
index 0000000000..ce68443c39
--- /dev/null
+++ b/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb
@@ -0,0 +1,9 @@
+module Rails
+ module Generators
+ class StylesheetsGenerator < Base
+ def copy_stylesheets_file
+ template "scaffold.css", "public/stylesheets/scaffold.css" if behavior == :invoke
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/style.css b/railties/lib/generators/rails/stylesheets/templates/scaffold.css
index 093c20994d..093c20994d 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/style.css
+++ b/railties/lib/generators/rails/stylesheets/templates/scaffold.css
diff --git a/railties/lib/generators/test_unit.rb b/railties/lib/generators/test_unit.rb
new file mode 100644
index 0000000000..2763feb017
--- /dev/null
+++ b/railties/lib/generators/test_unit.rb
@@ -0,0 +1,8 @@
+require 'generators/named_base'
+
+module TestUnit
+ module Generators
+ class Base < Rails::Generators::NamedBase #:nodoc:
+ end
+ end
+end
diff --git a/railties/lib/generators/test_unit/controller/controller_generator.rb b/railties/lib/generators/test_unit/controller/controller_generator.rb
new file mode 100644
index 0000000000..b57a6e794f
--- /dev/null
+++ b/railties/lib/generators/test_unit/controller/controller_generator.rb
@@ -0,0 +1,14 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class ControllerGenerator < Base
+ check_class_collision :suffix => "ControllerTest"
+
+ def create_test_files
+ template 'functional_test.rb',
+ File.join('test/functional', class_path, "#{file_name}_controller_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb b/railties/lib/generators/test_unit/controller/templates/functional_test.rb
index 62fa5d86fd..62fa5d86fd 100644
--- a/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb
+++ b/railties/lib/generators/test_unit/controller/templates/functional_test.rb
diff --git a/railties/lib/generators/test_unit/helper/helper_generator.rb b/railties/lib/generators/test_unit/helper/helper_generator.rb
new file mode 100644
index 0000000000..9ecfaa45ab
--- /dev/null
+++ b/railties/lib/generators/test_unit/helper/helper_generator.rb
@@ -0,0 +1,13 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class HelperGenerator < Base
+ check_class_collision :suffix => "HelperTest"
+
+ def create_helper_files
+ template 'helper_test.rb', File.join('test/unit/helpers', class_path, "#{file_name}_helper_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb b/railties/lib/generators/test_unit/helper/templates/helper_test.rb
index 591e40900e..591e40900e 100644
--- a/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb
+++ b/railties/lib/generators/test_unit/helper/templates/helper_test.rb
diff --git a/railties/lib/generators/test_unit/integration/integration_generator.rb b/railties/lib/generators/test_unit/integration/integration_generator.rb
new file mode 100644
index 0000000000..d9d9b3bf1d
--- /dev/null
+++ b/railties/lib/generators/test_unit/integration/integration_generator.rb
@@ -0,0 +1,13 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class IntegrationGenerator < Base
+ check_class_collision :suffix => "Test"
+
+ def create_test_files
+ template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb b/railties/lib/generators/test_unit/integration/templates/integration_test.rb
index 2c57158b1c..2c57158b1c 100644
--- a/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb
+++ b/railties/lib/generators/test_unit/integration/templates/integration_test.rb
diff --git a/railties/lib/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/generators/test_unit/mailer/mailer_generator.rb
new file mode 100644
index 0000000000..ef350a6224
--- /dev/null
+++ b/railties/lib/generators/test_unit/mailer/mailer_generator.rb
@@ -0,0 +1,21 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class MailerGenerator < Base
+ argument :actions, :type => :array, :default => [], :banner => "method method"
+ check_class_collision :suffix => "Test"
+
+ def create_test_files
+ template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb")
+ end
+
+ def create_fixtures_files
+ actions.each do |action|
+ @action, @path = action, File.join(file_path, action)
+ template "fixture", File.join("test/fixtures", @path)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/generators/test_unit/mailer/templates/fixture b/railties/lib/generators/test_unit/mailer/templates/fixture
new file mode 100644
index 0000000000..fcce7bd805
--- /dev/null
+++ b/railties/lib/generators/test_unit/mailer/templates/fixture
@@ -0,0 +1,3 @@
+<%= class_name %>#<%= @action %>
+
+Find me in app/views/<%= @path %>
diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb b/railties/lib/generators/test_unit/mailer/templates/unit_test.rb
index 4de94076e9..4de94076e9 100644
--- a/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb
+++ b/railties/lib/generators/test_unit/mailer/templates/unit_test.rb
diff --git a/railties/lib/generators/test_unit/model/model_generator.rb b/railties/lib/generators/test_unit/model/model_generator.rb
new file mode 100644
index 0000000000..469306e6c5
--- /dev/null
+++ b/railties/lib/generators/test_unit/model/model_generator.rb
@@ -0,0 +1,24 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class ModelGenerator < Base
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ class_option :fixture, :type => :boolean
+
+ check_class_collision :suffix => "Test"
+
+ def create_test_file
+ template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
+ end
+
+ hook_for :fixture_replacement
+
+ def create_fixture_file
+ if options[:fixture] && options[:fixture_replacement].nil?
+ template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml")
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml b/railties/lib/generators/test_unit/model/templates/fixtures.yml
index c21035113e..c21035113e 100644
--- a/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml
+++ b/railties/lib/generators/test_unit/model/templates/fixtures.yml
diff --git a/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb b/railties/lib/generators/test_unit/model/templates/unit_test.rb
index 3e0bc29d3a..3e0bc29d3a 100644
--- a/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb
+++ b/railties/lib/generators/test_unit/model/templates/unit_test.rb
diff --git a/railties/lib/generators/test_unit/observer/observer_generator.rb b/railties/lib/generators/test_unit/observer/observer_generator.rb
new file mode 100644
index 0000000000..14181f4e49
--- /dev/null
+++ b/railties/lib/generators/test_unit/observer/observer_generator.rb
@@ -0,0 +1,13 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class ObserverGenerator < Base
+ check_class_collision :suffix => "ObserverTest"
+
+ def create_test_files
+ template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb b/railties/lib/generators/test_unit/observer/templates/unit_test.rb
index 03f6d5666e..03f6d5666e 100644
--- a/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb
+++ b/railties/lib/generators/test_unit/observer/templates/unit_test.rb
diff --git a/railties/lib/generators/test_unit/performance/performance_generator.rb b/railties/lib/generators/test_unit/performance/performance_generator.rb
new file mode 100644
index 0000000000..0d9c646b26
--- /dev/null
+++ b/railties/lib/generators/test_unit/performance/performance_generator.rb
@@ -0,0 +1,13 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class PerformanceGenerator < Base
+ check_class_collision :suffix => "Test"
+
+ def create_test_files
+ template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb b/railties/lib/generators/test_unit/performance/templates/performance_test.rb
index 27c91b0fca..27c91b0fca 100644
--- a/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb
+++ b/railties/lib/generators/test_unit/performance/templates/performance_test.rb
diff --git a/railties/lib/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/generators/test_unit/plugin/plugin_generator.rb
new file mode 100644
index 0000000000..05adf58c4f
--- /dev/null
+++ b/railties/lib/generators/test_unit/plugin/plugin_generator.rb
@@ -0,0 +1,13 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class PluginGenerator < Base
+ check_class_collision :suffix => "Test"
+
+ def create_test_files
+ directory '.', 'test'
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb b/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt
index 3e0bc29d3a..3e0bc29d3a 100644
--- a/railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb
+++ b/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb b/railties/lib/generators/test_unit/plugin/templates/test_helper.rb
index cf148b8b47..cf148b8b47 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb
+++ b/railties/lib/generators/test_unit/plugin/templates/test_helper.rb
diff --git a/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..78fcea1e9c
--- /dev/null
+++ b/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb
@@ -0,0 +1,17 @@
+require 'generators/test_unit'
+
+module TestUnit
+ module Generators
+ class ScaffoldGenerator < Base
+ include Rails::Generators::ScaffoldBase
+
+ class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller"
+ check_class_collision :suffix => "ControllerTest"
+
+ def create_test_files
+ template 'functional_test.rb',
+ File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb b/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb
index cd2fc578bf..e4bf4035da 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb
+++ b/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb
@@ -1,11 +1,13 @@
require 'test_helper'
class <%= controller_class_name %>ControllerTest < ActionController::TestCase
+<% unless options[:singleton] -%>
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:<%= table_name %>)
end
+<% end -%>
test "should get new" do
get :new
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index f0fb78c8f4..336bff9534 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -244,7 +244,6 @@ module Rails
if RUBY_VERSION < '1.9'
$KCODE='u'
else
- Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
end
end
@@ -568,4 +567,13 @@ Run `rake gems:install` to install the missing gems.
ActiveSupport::Dependencies.unhook!
end
end
+
+ # Configure generators if they were already loaded
+ Initializer.default.add :initialize_generators do
+ if defined?(Rails::Generators)
+ Rails::Generators.no_color! unless config.generators.colorize_logging
+ Rails::Generators.aliases.deep_merge! config.generators.aliases
+ Rails::Generators.options.deep_merge! config.generators.options
+ end
+ end
end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 1a2f217d20..fe3cb67d3a 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -249,5 +249,51 @@ module Rails
def reload_plugins?
@reload_plugins
end
+
+ # Holds generators configuration:
+ #
+ # config.generators do |g|
+ # g.orm :datamapper, :migration => true
+ # g.template_engine :haml
+ # g.test_framework :rspec
+ # end
+ #
+ # If you want to disable color in console, do:
+ #
+ # config.generators.colorize_logging = false
+ #
+ def generators
+ @generators ||= Generators.new
+ if block_given?
+ yield @generators
+ else
+ @generators
+ end
+ end
+
+ class Generators #:nodoc:
+ attr_accessor :aliases, :options, :colorize_logging
+
+ def initialize
+ @aliases = Hash.new { |h,k| h[k] = {} }
+ @options = Hash.new { |h,k| h[k] = {} }
+ @colorize_logging = true
+ end
+
+ def method_missing(method, *args)
+ method = method.to_s.sub(/=$/, '').to_sym
+ namespace = args.first.is_a?(Symbol) ? args.shift : nil
+ configuration = args.first.is_a?(Hash) ? args.shift : nil
+
+ @options[:rails][method] = namespace if namespace
+ namespace ||= method
+
+ if configuration
+ aliases = configuration.delete(:aliases)
+ @aliases[namespace].merge!(aliases) if aliases
+ @options[namespace].merge!(configuration)
+ end
+ end
+ end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/rack/metal.rb b/railties/lib/rails/rack/metal.rb
index b031be29af..6c0732f732 100644
--- a/railties/lib/rails/rack/metal.rb
+++ b/railties/lib/rails/rack/metal.rb
@@ -11,6 +11,9 @@ module Rails
cattr_accessor :metal_paths
self.metal_paths = ["#{Rails.root}/app/metal"]
cattr_accessor :requested_metals
+
+ cattr_accessor :pass_through_on
+ self.pass_through_on = 404
def self.metals
matcher = /#{Regexp.escape('/app/metal/')}(.*)\.rb\Z/
@@ -36,6 +39,9 @@ module Rails
def initialize(app)
@app = app
+ @pass_through_on = {}
+ [*self.class.pass_through_on].each { |status| @pass_through_on[status] = true }
+
@metals = ActiveSupport::OrderedHash.new
self.class.metals.each { |app| @metals[app] = true }
freeze
@@ -44,7 +50,7 @@ module Rails
def call(env)
@metals.keys.each do |app|
result = app.call(env)
- return result unless result[0].to_i == 404
+ return result unless @pass_through_on.include?(result[0].to_i)
end
@app.call(env)
end
diff --git a/railties/lib/rails_generator.rb b/railties/lib/rails_generator.rb
deleted file mode 100644
index 85400932dd..0000000000
--- a/railties/lib/rails_generator.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-#--
-# Copyright (c) 2004 Jeremy Kemper
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#++
-
-activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
-$:.unshift(activesupport_path) if File.directory?(activesupport_path)
-require 'active_support/all'
-
-$:.unshift(File.dirname(__FILE__))
-require 'rails_generator/base'
-require 'rails_generator/lookup'
-require 'rails_generator/commands'
-
-Rails::Generator::Base.send(:include, Rails::Generator::Lookup)
-Rails::Generator::Base.send(:include, Rails::Generator::Commands)
-
-# Set up a default logger for convenience.
-require 'rails_generator/simple_logger'
-Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(STDOUT)
diff --git a/railties/lib/rails_generator/base.rb b/railties/lib/rails_generator/base.rb
deleted file mode 100644
index aa7081f8da..0000000000
--- a/railties/lib/rails_generator/base.rb
+++ /dev/null
@@ -1,266 +0,0 @@
-require File.dirname(__FILE__) + '/options'
-require File.dirname(__FILE__) + '/manifest'
-require File.dirname(__FILE__) + '/spec'
-require File.dirname(__FILE__) + '/generated_attribute'
-
-module Rails
- # Rails::Generator is a code generation platform tailored for the Rails
- # web application framework. Generators are easily invoked within Rails
- # applications to add and remove components such as models and controllers.
- # New generators are easy to create and may be distributed as RubyGems,
- # tarballs, or Rails plugins for inclusion system-wide, per-user,
- # or per-application.
- #
- # For actual examples see the rails_generator/generators directory in the
- # Rails source (or the +railties+ directory if you have frozen the Rails
- # source in your application).
- #
- # Generators may subclass other generators to provide variations that
- # require little or no new logic but replace the template files.
- #
- # For a RubyGem, put your generator class and templates in the +lib+
- # directory. For a Rails plugin, make a +generators+ directory at the
- # root of your plugin.
- #
- # The layout of generator files can be seen in the built-in
- # +controller+ generator:
- #
- # generators/
- # components/
- # controller/
- # controller_generator.rb
- # templates/
- # controller.rb
- # functional_test.rb
- # helper.rb
- # view.html.erb
- #
- # The directory name (+controller+) matches the name of the generator file
- # (controller_generator.rb) and class (ControllerGenerator). The files
- # that will be copied or used as templates are stored in the +templates+
- # directory.
- #
- # The filenames of the templates don't matter, but choose something that
- # will be self-explanatory since you will be referencing these in the
- # +manifest+ method inside your generator subclass.
- #
- #
- module Generator
- class GeneratorError < StandardError; end
- class UsageError < GeneratorError; end
-
-
- # The base code generator is bare-bones. It sets up the source and
- # destination paths and tells the logger whether to keep its trap shut.
- #
- # It's useful for copying files such as stylesheets, images, or
- # javascripts.
- #
- # For more comprehensive template-based passive code generation with
- # arguments, you'll want Rails::Generator::NamedBase.
- #
- # Generators create a manifest of the actions they perform then hand
- # the manifest to a command which replays the actions to do the heavy
- # lifting (such as checking for existing files or creating directories
- # if needed). Create, destroy, and list commands are included. Since a
- # single manifest may be used by any command, creating new generators is
- # as simple as writing some code templates and declaring what you'd like
- # to do with them.
- #
- # The manifest method must be implemented by subclasses, returning a
- # Rails::Generator::Manifest. The +record+ method is provided as a
- # convenience for manifest creation. Example:
- #
- # class StylesheetGenerator < Rails::Generator::Base
- # def manifest
- # record do |m|
- # m.directory('public/stylesheets')
- # m.file('application.css', 'public/stylesheets/application.css')
- # end
- # end
- # end
- #
- # See Rails::Generator::Commands::Create for a list of methods available
- # to the manifest.
- class Base
- include Options
-
- # Declare default options for the generator. These options
- # are inherited to subclasses.
- default_options :collision => :ask, :quiet => false
-
- # A logger instance available everywhere in the generator.
- cattr_accessor :logger
-
- # Every generator that is dynamically looked up is tagged with a
- # Spec describing where it was found.
- class_inheritable_accessor :spec
-
- attr_reader :source_root, :destination_root, :args
-
- def initialize(runtime_args, runtime_options = {})
- @args = runtime_args
- parse!(@args, runtime_options)
-
- # Derive source and destination paths.
- @source_root = options[:source] || File.join(spec.path, 'templates')
- if options[:destination]
- @destination_root = options[:destination]
- elsif defined? ::RAILS_ROOT
- @destination_root = ::RAILS_ROOT
- end
-
- # Silence the logger if requested.
- logger.quiet = options[:quiet]
-
- # Raise usage error if help is requested.
- usage if options[:help]
- end
-
- # Generators must provide a manifest. Use the +record+ method to create
- # a new manifest and record your generator's actions.
- def manifest
- raise NotImplementedError, "No manifest for '#{spec.name}' generator."
- end
-
- # Return the full path from the source root for the given path.
- # Example for source_root = '/source':
- # source_path('some/path.rb') == '/source/some/path.rb'
- #
- # The given path may include a colon ':' character to indicate that
- # the file belongs to another generator. This notation allows any
- # generator to borrow files from another. Example:
- # source_path('model:fixture.yml') = '/model/source/path/fixture.yml'
- def source_path(relative_source)
- # Check whether we're referring to another generator's file.
- name, path = relative_source.split(':', 2)
-
- # If not, return the full path to our source file.
- if path.nil?
- File.join(source_root, name)
-
- # Otherwise, ask our referral for the file.
- else
- # FIXME: this is broken, though almost always true. Others'
- # source_root are not necessarily the templates dir.
- File.join(self.class.lookup(name).path, 'templates', path)
- end
- end
-
- # Return the full path from the destination root for the given path.
- # Example for destination_root = '/dest':
- # destination_path('some/path.rb') == '/dest/some/path.rb'
- def destination_path(relative_destination)
- File.join(destination_root, relative_destination)
- end
-
- def after_generate
- end
-
- protected
- # Convenience method for generator subclasses to record a manifest.
- def record
- Rails::Generator::Manifest.new(self) { |m| yield m }
- end
-
- # Override with your own usage banner.
- def banner
- "Usage: #{$0} #{spec.name} [options]"
- end
-
- # Read USAGE from file in generator base path.
- def usage_message
- File.read(File.join(spec.path, 'USAGE')) rescue ''
- end
- end
-
-
- # The base generator for named components: models, controllers, mailers,
- # etc. The target name is taken as the first argument and inflected to
- # singular, plural, class, file, and table forms for your convenience.
- # The remaining arguments are aliased to +actions+ as an array for
- # controller and mailer convenience.
- #
- # Several useful local variables and methods are populated in the
- # +initialize+ method. See below for a list of Attributes and
- # External Aliases available to both the manifest and to all templates.
- #
- # If no name is provided, the generator raises a usage error with content
- # optionally read from the USAGE file in the generator's base path.
- #
- # For example, the +controller+ generator takes the first argument as
- # the name of the class and subsequent arguments as the names of
- # actions to be generated:
- #
- # ./script/generate controller Article index new create
- #
- # See Rails::Generator::Base for a discussion of manifests,
- # Rails::Generator::Commands::Create for methods available to the manifest,
- # and Rails::Generator for a general discussion of generators.
- class NamedBase < Base
- attr_reader :name, :class_name, :singular_name, :plural_name, :table_name
- attr_reader :class_path, :file_path, :class_nesting, :class_nesting_depth
- alias_method :file_name, :singular_name
- alias_method :actions, :args
-
- def initialize(runtime_args, runtime_options = {})
- super
-
- # Name argument is required.
- usage if runtime_args.empty?
-
- @args = runtime_args.dup
- base_name = @args.shift
- assign_names!(base_name)
- end
-
- protected
- # Override with your own usage banner.
- def banner
- "Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]"
- end
-
- def attributes
- @attributes ||= @args.collect do |attribute|
- Rails::Generator::GeneratedAttribute.new(*attribute.split(":"))
- end
- end
-
-
- private
- def assign_names!(name)
- @name = name
- base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name)
- @class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name)
- @table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name
- @table_name.gsub! '/', '_'
- if @class_nesting.empty?
- @class_name = @class_name_without_nesting
- else
- @table_name = @class_nesting.underscore << "_" << @table_name
- @class_name = "#{@class_nesting}::#{@class_name_without_nesting}"
- end
- end
-
- # Extract modules from filesystem-style or ruby-style path:
- # good/fun/stuff
- # Good::Fun::Stuff
- # produce the same results.
- def extract_modules(name)
- modules = name.include?('/') ? name.split('/') : name.split('::')
- name = modules.pop
- path = modules.map { |m| m.underscore }
- file_path = (path + [name.underscore]).join('/')
- nesting = modules.map { |m| m.camelize }.join('::')
- [name, path, file_path, nesting, modules.size]
- end
-
- def inflect_names(name)
- camel = name.camelize
- under = camel.underscore
- plural = under.pluralize
- [camel, under, plural]
- end
- end
- end
-end
diff --git a/railties/lib/rails_generator/commands.rb b/railties/lib/rails_generator/commands.rb
deleted file mode 100644
index b684dc92be..0000000000
--- a/railties/lib/rails_generator/commands.rb
+++ /dev/null
@@ -1,621 +0,0 @@
-require 'delegate'
-require 'optparse'
-require 'fileutils'
-require 'tempfile'
-require 'erb'
-
-module Rails
- module Generator
- module Commands
- # Here's a convenient way to get a handle on generator commands.
- # Command.instance('destroy', my_generator) instantiates a Destroy
- # delegate of my_generator ready to do your dirty work.
- def self.instance(command, generator)
- const_get(command.to_s.camelize).new(generator)
- end
-
- # Even more convenient access to commands. Include Commands in
- # the generator Base class to get a nice #command instance method
- # which returns a delegate for the requested command.
- def self.included(base)
- base.send(:define_method, :command) do |command|
- Commands.instance(command, self)
- end
- end
-
-
- # Generator commands delegate Rails::Generator::Base and implement
- # a standard set of actions. Their behavior is defined by the way
- # they respond to these actions: Create brings life; Destroy brings
- # death; List passively observes.
- #
- # Commands are invoked by replaying (or rewinding) the generator's
- # manifest of actions. See Rails::Generator::Manifest and
- # Rails::Generator::Base#manifest method that generator subclasses
- # are required to override.
- #
- # Commands allows generators to "plug in" invocation behavior, which
- # corresponds to the GoF Strategy pattern.
- class Base < DelegateClass(Rails::Generator::Base)
- # Replay action manifest. RewindBase subclass rewinds manifest.
- def invoke!
- manifest.replay(self)
- after_generate
- end
-
- def dependency(generator_name, args, runtime_options = {})
- logger.dependency(generator_name) do
- self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke!
- end
- end
-
- # Does nothing for all commands except Create.
- def class_collisions(*class_names)
- end
-
- # Does nothing for all commands except Create.
- def readme(*args)
- end
-
- protected
- def current_migration_number
- Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path|
- n = File.basename(file_path).split('_', 2).first.to_i
- if n > max then n else max end
- end
- end
-
- def next_migration_number
- current_migration_number + 1
- end
-
- def migration_directory(relative_path)
- directory(@migration_directory = relative_path)
- end
-
- def existing_migrations(file_name)
- Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/)
- end
-
- def migration_exists?(file_name)
- not existing_migrations(file_name).empty?
- end
-
- def next_migration_string(padding = 3)
- if ActiveRecord::Base.timestamped_migrations
- Time.now.utc.strftime("%Y%m%d%H%M%S")
- else
- "%.#{padding}d" % next_migration_number
- end
- end
-
- def gsub_file(relative_destination, regexp, *args, &block)
- path = destination_path(relative_destination)
- content = File.read(path).gsub(regexp, *args, &block)
- File.open(path, 'wb') { |file| file.write(content) }
- end
-
- private
- # Ask the user interactively whether to force collision.
- def force_file_collision?(destination, src, dst, file_options = {}, &block)
- $stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] "
- case $stdin.gets.chomp
- when /\Ad\z/i
- Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp|
- temp.write render_file(src, file_options, &block)
- temp.rewind
- $stdout.puts `#{diff_cmd} "#{dst}" "#{temp.path}"`
- end
- puts "retrying"
- raise 'retry diff'
- when /\Aa\z/i
- $stdout.puts "forcing #{spec.name}"
- options[:collision] = :force
- when /\Aq\z/i
- $stdout.puts "aborting #{spec.name}"
- raise SystemExit
- when /\An\z/i then :skip
- when /\Ay\z/i then :force
- else
- $stdout.puts <<-HELP
-Y - yes, overwrite
-n - no, do not overwrite
-a - all, overwrite this and all others
-q - quit, abort
-d - diff, show the differences between the old and the new
-h - help, show this help
-HELP
- raise 'retry'
- end
- rescue
- retry
- end
-
- def diff_cmd
- ENV['RAILS_DIFF'] || 'diff -u'
- end
-
- def render_template_part(template_options)
- # Getting Sandbox to evaluate part template in it
- part_binding = template_options[:sandbox].call.sandbox_binding
- part_rel_path = template_options[:insert]
- part_path = source_path(part_rel_path)
-
- # Render inner template within Sandbox binding
- rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding)
- begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id])
- end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id])
- begin_mark + rendered_part + end_mark
- end
-
- def template_part_mark(name, id)
- "<!--[#{name}:#{id}]-->\n"
- end
- end
-
- # Base class for commands which handle generator actions in reverse, such as Destroy.
- class RewindBase < Base
- # Rewind action manifest.
- def invoke!
- manifest.rewind(self)
- end
- end
-
-
- # Create is the premier generator command. It copies files, creates
- # directories, renders templates, and more.
- class Create < Base
-
- # Check whether the given class names are already taken by
- # Ruby or Rails. In the future, expand to check other namespaces
- # such as the rest of the user's app.
- def class_collisions(*class_names)
- path = class_names.shift
- class_names.flatten.each do |class_name|
- # Convert to string to allow symbol arguments.
- class_name = class_name.to_s
-
- # Skip empty strings.
- next if class_name.strip.empty?
-
- # Split the class from its module nesting.
- nesting = class_name.split('::')
- name = nesting.pop
-
- # Hack to limit const_defined? to non-inherited on 1.9.
- extra = []
- extra << false unless Object.method(:const_defined?).arity == 1
-
- # Extract the last Module in the nesting.
- last = nesting.inject(Object) { |last, nest|
- break unless last.const_defined?(nest, *extra)
- last.const_get(nest)
- }
-
- # If the last Module exists, check whether the given
- # class exists and raise a collision if so.
- if last and last.const_defined?(name.camelize, *extra)
- raise_class_collision(class_name)
- end
- end
- end
-
- # Copy a file from source to destination with collision checking.
- #
- # The file_options hash accepts :chmod and :shebang and :collision options.
- # :chmod sets the permissions of the destination file:
- # file 'config/empty.log', 'log/test.log', :chmod => 0664
- # :shebang sets the #!/usr/bin/ruby line for scripts
- # file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby'
- # :collision sets the collision option only for the destination file:
- # file 'settings/server.yml', 'config/server.yml', :collision => :skip
- #
- # Collisions are handled by checking whether the destination file
- # exists and either skipping the file, forcing overwrite, or asking
- # the user what to do.
- def file(relative_source, relative_destination, file_options = {}, &block)
- # Determine full paths for source and destination files.
- source = source_path(relative_source)
- destination = destination_path(relative_destination)
- destination_exists = File.exist?(destination)
-
- # If source and destination are identical then we're done.
- if destination_exists and identical?(source, destination, &block)
- return logger.identical(relative_destination)
- end
-
- # Check for and resolve file collisions.
- if destination_exists
-
- # Make a choice whether to overwrite the file. :force and
- # :skip already have their mind made up, but give :ask a shot.
- choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask
- when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block)
- when :force then :force
- when :skip then :skip
- else raise "Invalid collision option: #{options[:collision].inspect}"
- end
-
- # Take action based on our choice. Bail out if we chose to
- # skip the file; otherwise, log our transgression and continue.
- case choice
- when :force then logger.force(relative_destination)
- when :skip then return(logger.skip(relative_destination))
- else raise "Invalid collision choice: #{choice}.inspect"
- end
-
- # File doesn't exist so log its unbesmirched creation.
- else
- logger.create relative_destination
- end
-
- # If we're pretending, back off now.
- return if options[:pretend]
-
- # Write destination file with optional shebang. Yield for content
- # if block given so templaters may render the source file. If a
- # shebang is requested, replace the existing shebang or insert a
- # new one.
- File.open(destination, 'wb') do |dest|
- dest.write render_file(source, file_options, &block)
- end
-
- # Optionally change permissions.
- if file_options[:chmod]
- FileUtils.chmod(file_options[:chmod], destination)
- end
-
- # Optionally add file to subversion or git
- system("svn add #{destination}") if options[:svn]
- system("git add -v #{relative_destination}") if options[:git]
- end
-
- # Checks if the source and the destination file are identical. If
- # passed a block then the source file is a template that needs to first
- # be evaluated before being compared to the destination.
- def identical?(source, destination, &block)
- return false if File.directory? destination
- source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source)
- destination = IO.read(destination)
- source == destination
- end
-
- # Generate a file for a Rails application using an ERuby template.
- # Looks up and evaluates a template by name and writes the result.
- #
- # The ERB template uses explicit trim mode to best control the
- # proliferation of whitespace in generated code. <%- trims leading
- # whitespace; -%> trims trailing whitespace including one newline.
- #
- # A hash of template options may be passed as the last argument.
- # The options accepted by the file are accepted as well as :assigns,
- # a hash of variable bindings. Example:
- # template 'foo', 'bar', :assigns => { :action => 'view' }
- #
- # Template is implemented in terms of file. It calls file with a
- # block which takes a file handle and returns its rendered contents.
- def template(relative_source, relative_destination, template_options = {})
- file(relative_source, relative_destination, template_options) do |file|
- # Evaluate any assignments in a temporary, throwaway binding.
- vars = template_options[:assigns] || {}
- b = template_options[:binding] || binding
- vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
-
- # Render the source file with the temporary binding.
- ERB.new(file.read, nil, '-').result(b)
- end
- end
-
- def complex_template(relative_source, relative_destination, template_options = {})
- options = template_options.dup
- options[:assigns] ||= {}
- options[:assigns]['template_for_inclusion'] = render_template_part(template_options)
- template(relative_source, relative_destination, options)
- end
-
- # Create a directory including any missing parent directories.
- # Always skips directories which exist.
- def directory(relative_path)
- path = destination_path(relative_path)
- if File.exist?(path)
- logger.exists relative_path
- else
- logger.create relative_path
- unless options[:pretend]
- FileUtils.mkdir_p(path)
- # git doesn't require adding the paths, adding the files later will
- # automatically do a path add.
-
- # Subversion doesn't do path adds, so we need to add
- # each directory individually.
- # So stack up the directory tree and add the paths to
- # subversion in order without recursion.
- if options[:svn]
- stack = [relative_path]
- until File.dirname(stack.last) == stack.last # dirname('.') == '.'
- stack.push File.dirname(stack.last)
- end
- stack.reverse_each do |rel_path|
- svn_path = destination_path(rel_path)
- system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
- end
- end
- end
- end
- end
-
- # Display a README.
- def readme(*relative_sources)
- relative_sources.flatten.each do |relative_source|
- logger.readme relative_source
- puts File.read(source_path(relative_source)) unless options[:pretend]
- end
- end
-
- # When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
- def migration_template(relative_source, relative_destination, template_options = {})
- migration_directory relative_destination
- migration_file_name = template_options[:migration_file_name] || file_name
- raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name)
- template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options)
- end
-
- def route_resources(*resources)
- resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
- sentinel = 'ActionController::Routing::Routes.draw do |map|'
-
- logger.route "map.resources #{resource_list}"
- unless options[:pretend]
- gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
- "#{match}\n map.resources #{resource_list}\n"
- end
- end
- end
-
- private
- def render_file(path, options = {})
- File.open(path, 'rb') do |file|
- if block_given?
- yield file
- else
- content = ''
- if shebang = options[:shebang]
- content << "#!#{shebang}\n"
- if line = file.gets
- content << "line\n" if line !~ /^#!/
- end
- end
- content << file.read
- end
- end
- end
-
- # Raise a usage error with an informative WordNet suggestion.
- # Thanks to Florian Gross (flgr).
- def raise_class_collision(class_name)
- message = <<end_message
- The name '#{class_name}' is either already used in your application or reserved by Ruby on Rails.
- Please choose an alternative and run this generator again.
-end_message
- if suggest = find_synonyms(class_name)
- if suggest.any?
- message << "\n Suggestions: \n\n"
- message << suggest.join("\n")
- end
- end
- raise UsageError, message
- end
-
- SYNONYM_LOOKUP_URI = "http://wordnet.princeton.edu/perl/webwn?s=%s"
-
- # Look up synonyms on WordNet. Thanks to Florian Gross (flgr).
- def find_synonyms(word)
- require 'open-uri'
- require 'timeout'
- timeout(5) do
- open(SYNONYM_LOOKUP_URI % word) do |stream|
- # Grab words linked to dictionary entries as possible synonyms
- data = stream.read.gsub("&nbsp;", " ").scan(/<a href="webwn.*?">([\w ]*?)<\/a>/s).uniq
- end
- end
- rescue Exception
- return nil
- end
- end
-
-
- # Undo the actions performed by a generator. Rewind the action
- # manifest and attempt to completely erase the results of each action.
- class Destroy < RewindBase
- # Remove a file if it exists and is a file.
- def file(relative_source, relative_destination, file_options = {})
- destination = destination_path(relative_destination)
- if File.exist?(destination)
- logger.rm relative_destination
- unless options[:pretend]
- if options[:svn]
- # If the file has been marked to be added
- # but has not yet been checked in, revert and delete
- if options[:svn][relative_destination]
- system("svn revert #{destination}")
- FileUtils.rm(destination)
- else
- # If the directory is not in the status list, it
- # has no modifications so we can simply remove it
- system("svn rm #{destination}")
- end
- elsif options[:git]
- if options[:git][:new][relative_destination]
- # file has been added, but not committed
- system("git reset HEAD #{relative_destination}")
- FileUtils.rm(destination)
- elsif options[:git][:modified][relative_destination]
- # file is committed and modified
- system("git rm -f #{relative_destination}")
- else
- # If the directory is not in the status list, it
- # has no modifications so we can simply remove it
- system("git rm #{relative_destination}")
- end
- else
- FileUtils.rm(destination)
- end
- end
- else
- logger.missing relative_destination
- return
- end
- end
-
- # Templates are deleted just like files and the actions take the
- # same parameters, so simply alias the file method.
- alias_method :template, :file
-
- # Remove each directory in the given path from right to left.
- # Remove each subdirectory if it exists and is a directory.
- def directory(relative_path)
- parts = relative_path.split('/')
- until parts.empty?
- partial = File.join(parts)
- path = destination_path(partial)
- if File.exist?(path)
- if Dir[File.join(path, '*')].empty?
- logger.rmdir partial
- unless options[:pretend]
- if options[:svn]
- # If the directory has been marked to be added
- # but has not yet been checked in, revert and delete
- if options[:svn][relative_path]
- system("svn revert #{path}")
- FileUtils.rmdir(path)
- else
- # If the directory is not in the status list, it
- # has no modifications so we can simply remove it
- system("svn rm #{path}")
- end
- # I don't think git needs to remove directories?..
- # or maybe they have special consideration...
- else
- FileUtils.rmdir(path)
- end
- end
- else
- logger.notempty partial
- end
- else
- logger.missing partial
- end
- parts.pop
- end
- end
-
- def complex_template(*args)
- # nothing should be done here
- end
-
- # When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}".
- def migration_template(relative_source, relative_destination, template_options = {})
- migration_directory relative_destination
-
- migration_file_name = template_options[:migration_file_name] || file_name
- unless migration_exists?(migration_file_name)
- puts "There is no migration named #{migration_file_name}"
- return
- end
-
-
- existing_migrations(migration_file_name).each do |file_path|
- file(relative_source, file_path, template_options)
- end
- end
-
- def route_resources(*resources)
- resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
- look_for = "\n map.resources #{resource_list}\n"
- logger.route "map.resources #{resource_list}"
- gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
- end
- end
-
-
- # List a generator's action manifest.
- class List < Base
- def dependency(generator_name, args, options = {})
- logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})"
- end
-
- def class_collisions(*class_names)
- logger.class_collisions class_names.join(', ')
- end
-
- def file(relative_source, relative_destination, options = {})
- logger.file relative_destination
- end
-
- def template(relative_source, relative_destination, options = {})
- logger.template relative_destination
- end
-
- def complex_template(relative_source, relative_destination, options = {})
- logger.template "#{options[:insert]} inside #{relative_destination}"
- end
-
- def directory(relative_path)
- logger.directory "#{destination_path(relative_path)}/"
- end
-
- def readme(*args)
- logger.readme args.join(', ')
- end
-
- def migration_template(relative_source, relative_destination, options = {})
- migration_directory relative_destination
- logger.migration_template file_name
- end
-
- def route_resources(*resources)
- resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
- logger.route "map.resources #{resource_list}"
- end
- end
-
- # Update generator's action manifest.
- class Update < Create
- def file(relative_source, relative_destination, options = {})
- # logger.file relative_destination
- end
-
- def template(relative_source, relative_destination, options = {})
- # logger.template relative_destination
- end
-
- def complex_template(relative_source, relative_destination, template_options = {})
-
- begin
- dest_file = destination_path(relative_destination)
- source_to_update = File.readlines(dest_file).join
- rescue Errno::ENOENT
- logger.missing relative_destination
- return
- end
-
- logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}"
-
- begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id]))
- end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id]))
-
- # Refreshing inner part of the template with freshly rendered part.
- rendered_part = render_template_part(template_options)
- source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part)
-
- File.open(dest_file, 'w') { |file| file.write(source_to_update) }
- end
-
- def directory(relative_path)
- # logger.directory "#{destination_path(relative_path)}/"
- end
- end
-
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
deleted file mode 100644
index c8c2239f34..0000000000
--- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb
+++ /dev/null
@@ -1,263 +0,0 @@
-require 'rbconfig'
-require File.dirname(__FILE__) + '/template_runner'
-require 'digest/md5'
-require 'active_support/secure_random'
-
-class AppGenerator < Rails::Generator::Base
- DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
-
- DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db )
- DEFAULT_DATABASE = 'sqlite3'
-
- mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.."
- default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE),
- :shebang => DEFAULT_SHEBANG, :with_dispatchers => false, :freeze => false
-
-
- def initialize(runtime_args, runtime_options = {})
- super
-
- usage if args.empty?
- usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db]))
-
- @destination_root = args.shift
- @app_name = File.basename(File.expand_path(@destination_root))
- end
-
- def manifest
- record do |m|
- create_directories(m)
- create_root_files(m)
- create_app_files(m)
- create_config_files(m)
- create_script_files(m)
- create_test_files(m)
- create_public_files(m)
- create_documentation_file(m)
- create_log_files(m)
- end
- end
-
- def after_generate
- if options[:template]
- Rails::TemplateRunner.new(options[:template], @destination_root)
- end
- end
-
- protected
- def banner
- "Usage: #{$0} /path/to/your/app [options]"
- end
-
- def add_options!(opt)
- opt.separator ''
- opt.separator 'Options:'
- opt.on("-r", "--ruby=path", String,
- "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
- "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
-
- opt.on("-d", "--database=name", String,
- "Preconfigure for selected database (options: #{DATABASES.join('/')}).",
- "Default: #{DEFAULT_DATABASE}") { |v| options[:db] = v }
-
- opt.on("-D", "--with-dispatchers",
- "Add CGI/FastCGI/mod_ruby dispatches code to generated application skeleton",
- "Default: false") { |v| options[:with_dispatchers] = v }
-
- opt.on("-f", "--freeze",
- "Freeze Rails in vendor/rails from the gems generating the skeleton",
- "Default: false") { |v| options[:freeze] = v }
-
- opt.on("-m", "--template=path", String,
- "Use an application template that lives at path (can be a filesystem path or URL).",
- "Default: (none)") { |v| options[:template] = v }
-
- end
-
-
- private
- def create_directories(m)
- m.directory ''
-
- # Intermediate directories are automatically created so don't sweat their absence here.
- %w(
- app/controllers
- app/helpers
- app/models
- app/views/layouts
- config/environments
- config/initializers
- config/locales
- db
- doc
- lib
- lib/tasks
- log
- public/images
- public/javascripts
- public/stylesheets
- script/performance
- test/fixtures
- test/functional
- test/integration
- test/performance
- test/unit
- vendor
- vendor/plugins
- tmp/sessions
- tmp/sockets
- tmp/cache
- tmp/pids
- ).each { |path| m.directory(path) }
- end
-
- def create_root_files(m)
- m.file "fresh_rakefile", "Rakefile"
- m.file "README", "README"
- end
-
- def create_app_files(m)
- m.file "helpers/application_controller.rb", "app/controllers/application_controller.rb"
- m.file "helpers/application_helper.rb", "app/helpers/application_helper.rb"
- end
-
- def create_config_files(m)
- create_database_configuration_file(m)
- create_routes_file(m)
- create_locale_file(m)
- create_seeds_file(m)
- create_initializer_files(m)
- create_environment_files(m)
- end
-
- def create_documentation_file(m)
- m.file "doc/README_FOR_APP", "doc/README_FOR_APP"
- end
-
- def create_log_files(m)
- %w( server production development test ).each do |file|
- m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666
- end
- end
-
- def create_public_files(m)
- create_dispatch_files(m)
- create_error_files(m)
- create_welcome_file(m)
- create_browser_convention_files(m)
- create_rails_image(m)
- create_javascript_files(m)
- end
-
- def create_script_files(m)
- %w(
- about console dbconsole destroy generate runner server plugin
- performance/benchmarker performance/profiler
- ).each do |file|
- m.file "bin/#{file}", "script/#{file}", {
- :chmod => 0755,
- :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang]
- }
- end
- end
-
- def create_test_files(m)
- m.file "helpers/test_helper.rb", "test/test_helper.rb"
- m.file "helpers/performance_test.rb", "test/performance/browsing_test.rb"
- end
-
-
- def create_database_configuration_file(m)
- m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => {
- :app_name => @app_name,
- :socket => options[:db] == "mysql" ? mysql_socket_location : nil }
- end
-
- def create_routes_file(m)
- m.file "configs/routes.rb", "config/routes.rb"
- end
-
- def create_seeds_file(m)
- m.file "configs/seeds.rb", "db/seeds.rb"
- end
-
- def create_initializer_files(m)
- %w(
- backtrace_silencers
- inflections
- mime_types
- new_rails_defaults
- ).each do |initializer|
- m.file "configs/initializers/#{initializer}.rb", "config/initializers/#{initializer}.rb"
- end
-
- m.template "configs/initializers/session_store.rb", "config/initializers/session_store.rb",
- :assigns => { :app_name => @app_name, :app_secret => ActiveSupport::SecureRandom.hex(64) }
- end
-
- def create_locale_file(m)
- m.file "configs/locales/en.yml", "config/locales/en.yml"
- end
-
- def create_environment_files(m)
- m.template "environments/environment.rb", "config/environment.rb",
- :assigns => { :freeze => options[:freeze] }
-
- m.file "environments/boot.rb", "config/boot.rb"
- m.file "environments/production.rb", "config/environments/production.rb"
- m.file "environments/development.rb", "config/environments/development.rb"
- m.file "environments/test.rb", "config/environments/test.rb"
- end
-
-
- def create_dispatch_files(m)
- if options[:with_dispatchers]
- dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] }
-
- m.file "dispatches/config.ru", "config.ru"
- m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options
- m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options
- m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options
- end
- end
-
- def create_error_files(m)
- %w( 404 422 500 ).each do |file|
- m.file "html/#{file}.html", "public/#{file}.html"
- end
- end
-
- def create_welcome_file(m)
- m.file 'html/index.html', 'public/index.html'
- end
-
- def create_browser_convention_files(m)
- m.file "html/favicon.ico", "public/favicon.ico"
- m.file "html/robots.txt", "public/robots.txt"
- end
-
- def create_rails_image(m)
- m.file "html/images/rails.png", "public/images/rails.png"
- end
-
- def create_javascript_files(m)
- %w( prototype effects dragdrop controls application ).each do |javascript|
- m.file "html/javascripts/#{javascript}.js", "public/javascripts/#{javascript}.js"
- end
- end
-
-
- def mysql_socket_location
- [
- "/tmp/mysql.sock", # default
- "/var/run/mysqld/mysqld.sock", # debian/gentoo
- "/var/tmp/mysql.sock", # freebsd
- "/var/lib/mysql/mysql.sock", # fedora
- "/opt/local/lib/mysql/mysql.sock", # fedora
- "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
- "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
- "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
- "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
- ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
- end
-end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/applications/app/scm/git.rb b/railties/lib/rails_generator/generators/applications/app/scm/git.rb
deleted file mode 100644
index 445de6ab42..0000000000
--- a/railties/lib/rails_generator/generators/applications/app/scm/git.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Rails
- class Git < Scm
- def self.clone(repos, branch=nil)
- `git clone #{repos}`
-
- if branch
- `cd #{repos.split('/').last}/`
- `git checkout #{branch}`
- end
- end
-
- def self.run(command)
- `git #{command}`
- end
- end
-end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/applications/app/scm/scm.rb b/railties/lib/rails_generator/generators/applications/app/scm/scm.rb
deleted file mode 100644
index f6c08cad39..0000000000
--- a/railties/lib/rails_generator/generators/applications/app/scm/scm.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Rails
- class Scm
- private
- def self.hash_to_parameters(hash)
- hash.collect { |key, value| "--#{key} #{(value.kind_of?(String) ? value : "")}"}.join(" ")
- end
- end
-end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/applications/app/scm/svn.rb b/railties/lib/rails_generator/generators/applications/app/scm/svn.rb
deleted file mode 100644
index 22b5966d25..0000000000
--- a/railties/lib/rails_generator/generators/applications/app/scm/svn.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Rails
- class Svn < Scm
- def self.checkout(repos, branch = nil)
- `svn checkout #{repos}/#{branch || "trunk"}`
- end
- end
-end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/railties/lib/rails_generator/generators/applications/app/template_runner.rb
deleted file mode 100644
index 0e24d11950..0000000000
--- a/railties/lib/rails_generator/generators/applications/app/template_runner.rb
+++ /dev/null
@@ -1,401 +0,0 @@
-require File.dirname(__FILE__) + '/scm/scm'
-require File.dirname(__FILE__) + '/scm/git'
-require File.dirname(__FILE__) + '/scm/svn'
-
-require 'open-uri'
-require 'fileutils'
-
-module Rails
- class TemplateRunner
- attr_reader :root
- attr_writer :logger
-
- def initialize(template, root = '') # :nodoc:
- @root = File.expand_path(File.directory?(root) ? root : File.join(Dir.pwd, root))
-
- log 'applying', "template: #{template}"
-
- load_template(template)
-
- log 'applied', "#{template}"
- end
-
- def load_template(template)
- begin
- code = open(template).read
- in_root { self.instance_eval(code) }
- rescue LoadError, Errno::ENOENT => e
- raise "The template [#{template}] could not be loaded. Error: #{e}"
- end
- end
-
- # Create a new file in the Rails project folder. Specify the
- # relative path from RAILS_ROOT. Data is the return value of a block
- # or a data string.
- #
- # ==== Examples
- #
- # file("lib/fun_party.rb") do
- # hostname = ask("What is the virtual hostname I should use?")
- # "vhost.name = #{hostname}"
- # end
- #
- # file("config/apach.conf", "your apache config")
- #
- def file(filename, data = nil, log_action = true, &block)
- log 'file', filename if log_action
- dir, file = [File.dirname(filename), File.basename(filename)]
-
- inside(dir) do
- File.open(file, "w") do |f|
- if block_given?
- f.write(block.call)
- else
- f.write(data)
- end
- end
- end
- end
-
- # Install a plugin. You must provide either a Subversion url or Git url.
- # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned.
- #
- # ==== Examples
- #
- # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git'
- # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true
- # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
- #
- def plugin(name, options)
- log 'plugin', name
-
- if options[:git] && options[:submodule]
- in_root do
- Git.run("submodule add #{options[:git]} vendor/plugins/#{name}")
- end
- elsif options[:git] || options[:svn]
- in_root do
- run_ruby_script("script/plugin install #{options[:svn] || options[:git]}", false)
- end
- else
- log "! no git or svn provided for #{name}. skipping..."
- end
- end
-
- # Adds an entry into config/environment.rb for the supplied gem :
- def gem(name, options = {})
- log 'gem', name
- env = options.delete(:env)
-
- gems_code = "config.gem '#{name}'"
-
- if options.any?
- opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ")
- gems_code << ", #{opts}"
- end
-
- environment gems_code, :env => env
- end
-
- # Adds a line inside the Initializer block for config/environment.rb. Used by #gem
- # If options :env is specified, the line is appended to the corresponding
- # file in config/environments/#{env}.rb
- def environment(data = nil, options = {}, &block)
- sentinel = 'Rails::Initializer.run do |config|'
-
- data = block.call if !data && block_given?
-
- in_root do
- if options[:env].nil?
- gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
- "#{match}\n " << data
- end
- else
- Array.wrap(options[:env]).each do|env|
- append_file "config/environments/#{env}.rb", "\n#{data}"
- end
- end
- end
- end
-
- # Run a command in git.
- #
- # ==== Examples
- #
- # git :init
- # git :add => "this.file that.rb"
- # git :add => "onefile.rb", :rm => "badfile.cxx"
- #
- def git(command = {})
- in_root do
- if command.is_a?(Symbol)
- log 'running', "git #{command}"
- Git.run(command.to_s)
- else
- command.each do |command, options|
- log 'running', "git #{command} #{options}"
- Git.run("#{command} #{options}")
- end
- end
- end
- end
-
- # Create a new file in the vendor/ directory. Code can be specified
- # in a block or a data string can be given.
- #
- # ==== Examples
- #
- # vendor("sekrit.rb") do
- # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--"
- # "salt = '#{sekrit_salt}'"
- # end
- #
- # vendor("foreign.rb", "# Foreign code is fun")
- #
- def vendor(filename, data = nil, &block)
- log 'vendoring', filename
- file("vendor/#{filename}", data, false, &block)
- end
-
- # Create a new file in the lib/ directory. Code can be specified
- # in a block or a data string can be given.
- #
- # ==== Examples
- #
- # lib("crypto.rb") do
- # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
- # end
- #
- # lib("foreign.rb", "# Foreign code is fun")
- #
- def lib(filename, data = nil, &block)
- log 'lib', filename
- file("lib/#{filename}", data, false, &block)
- end
-
- # Create a new Rakefile with the provided code (either in a block or a string).
- #
- # ==== Examples
- #
- # rakefile("bootstrap.rake") do
- # project = ask("What is the UNIX name of your project?")
- #
- # <<-TASK
- # namespace :#{project} do
- # task :bootstrap do
- # puts "i like boots!"
- # end
- # end
- # TASK
- # end
- #
- # rakefile("seed.rake", "puts 'im plantin ur seedz'")
- #
- def rakefile(filename, data = nil, &block)
- log 'rakefile', filename
- file("lib/tasks/#{filename}", data, false, &block)
- end
-
- # Create a new initializer with the provided code (either in a block or a string).
- #
- # ==== Examples
- #
- # initializer("globals.rb") do
- # data = ""
- #
- # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do
- # data << "#{const} = :entp"
- # end
- #
- # data
- # end
- #
- # initializer("api.rb", "API_KEY = '123456'")
- #
- def initializer(filename, data = nil, &block)
- log 'initializer', filename
- file("config/initializers/#{filename}", data, false, &block)
- end
-
- # Generate something using a generator from Rails or a plugin.
- # The second parameter is the argument string that is passed to
- # the generator or an Array that is joined.
- #
- # ==== Example
- #
- # generate(:authenticated, "user session")
- #
- def generate(what, *args)
- log 'generating', what
- argument = args.map {|arg| arg.to_s }.flatten.join(" ")
-
- in_root { run_ruby_script("script/generate #{what} #{argument}", false) }
- end
-
- # Executes a command
- #
- # ==== Example
- #
- # inside('vendor') do
- # run('ln -s ~/edge rails)
- # end
- #
- def run(command, log_action = true)
- log 'executing', "#{command} from #{Dir.pwd}" if log_action
- `#{command}`
- end
-
- # Executes a ruby script (taking into account WIN32 platform quirks)
- def run_ruby_script(command, log_action = true)
- ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : ''
- run("#{ruby_command}#{command}", log_action)
- end
-
- # Runs the supplied rake task
- #
- # ==== Example
- #
- # rake("db:migrate")
- # rake("db:migrate", :env => "production")
- # rake("gems:install", :sudo => true)
- #
- def rake(command, options = {})
- log 'rake', command
- env = options[:env] || 'development'
- sudo = options[:sudo] ? 'sudo ' : ''
- in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) }
- end
-
- # Just run the capify command in root
- #
- # ==== Example
- #
- # capify!
- #
- def capify!
- log 'capifying'
- in_root { run('capify .', false) }
- end
-
- # Add Rails to /vendor/rails
- #
- # ==== Example
- #
- # freeze!
- #
- def freeze!(args = {})
- log 'vendor', 'rails edge'
- in_root { run('rake rails:freeze:edge', false) }
- end
-
- # Make an entry in Rails routing file conifg/routes.rb
- #
- # === Example
- #
- # route "map.root :controller => :welcome"
- #
- def route(routing_code)
- log 'route', routing_code
- sentinel = 'ActionController::Routing::Routes.draw do |map|'
-
- in_root do
- gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
- "#{match}\n #{routing_code}\n"
- end
- end
- end
-
- protected
-
- # Get a user's input
- #
- # ==== Example
- #
- # answer = ask("Should I freeze the latest Rails?")
- # freeze! if ask("Should I freeze the latest Rails?") == "yes"
- #
- def ask(string)
- log '', string
- STDIN.gets.strip
- end
-
- # Do something in the root of the Rails application or
- # a provided subfolder; the full path is yielded to the block you provide.
- # The path is set back to the previous path when the method exits.
- def inside(dir = '', &block)
- folder = File.join(root, dir)
- FileUtils.mkdir_p(folder) unless File.exist?(folder)
- FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield }
- end
-
- def in_root
- FileUtils.cd(root) { yield }
- end
-
- # Helper to test if the user says yes(y)?
- #
- # ==== Example
- #
- # freeze! if yes?("Should I freeze the latest Rails?")
- #
- def yes?(question)
- answer = ask(question).downcase
- answer == "y" || answer == "yes"
- end
-
- # Helper to test if the user does NOT say yes(y)?
- #
- # ==== Example
- #
- # capify! if no?("Will you be using vlad to deploy your application?")
- #
- def no?(question)
- !yes?(question)
- end
-
- # Run a regular expression replacement on a file
- #
- # ==== Example
- #
- # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
- #
- def gsub_file(relative_destination, regexp, *args, &block)
- path = destination_path(relative_destination)
- content = File.read(path).gsub(regexp, *args, &block)
- File.open(path, 'wb') { |file| file.write(content) }
- end
-
- # Append text to a file
- #
- # ==== Example
- #
- # append_file 'config/environments/test.rb', 'config.gem "rspec"'
- #
- def append_file(relative_destination, data)
- path = destination_path(relative_destination)
- File.open(path, 'ab') { |file| file.write(data) }
- end
-
- def destination_path(relative_destination)
- File.join(root, relative_destination)
- end
-
- def log(action, message = '')
- logger.log(action, message)
- end
-
- def logger
- @logger ||= Rails::Generator::Base.logger
- end
-
- def logger
- @logger ||= if defined?(Rails::Generator::Base)
- Rails::Generator::Base.logger
- else
- require 'rails_generator/simple_logger'
- Rails::Generator::SimpleLogger.new(STDOUT)
- end
- end
-
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/controller/USAGE b/railties/lib/rails_generator/generators/components/controller/USAGE
deleted file mode 100644
index 362872e84a..0000000000
--- a/railties/lib/rails_generator/generators/components/controller/USAGE
+++ /dev/null
@@ -1,30 +0,0 @@
-Description:
- Stubs out a new controller and its views. Pass the controller name, either
- CamelCased or under_scored, and a list of views as arguments.
-
- To create a controller within a module, specify the controller name as a
- path like 'parent_module/controller_name'.
-
- This generates a controller class in app/controllers, view templates in
- app/views/controller_name, a helper class in app/helpers, a functional
- test suite in test/functional and a helper test suite in test/unit/helpers.
-
-Example:
- `./script/generate controller CreditCard open debit credit close`
-
- Credit card controller with URLs like /credit_card/debit.
- Controller: app/controllers/credit_card_controller.rb
- Functional Test: test/functional/credit_card_controller_test.rb
- Views: app/views/credit_card/debit.html.erb [...]
- Helper: app/helpers/credit_card_helper.rb
- Helper Test: test/unit/helpers/credit_card_helper_test.rb
-
-Modules Example:
- `./script/generate controller 'admin/credit_card' suspend late_fee`
-
- Credit card admin controller with URLs /admin/credit_card/suspend.
- Controller: app/controllers/admin/credit_card_controller.rb
- Functional Test: test/functional/admin/credit_card_controller_test.rb
- Views: app/views/admin/credit_card/debit.html.erb [...]
- Helper: app/helpers/admin/credit_card_helper.rb
- Helper Test: test/unit/helpers/admin/credit_card_helper_test.rb
diff --git a/railties/lib/rails_generator/generators/components/controller/controller_generator.rb b/railties/lib/rails_generator/generators/components/controller/controller_generator.rb
deleted file mode 100644
index dc126e8a98..0000000000
--- a/railties/lib/rails_generator/generators/components/controller/controller_generator.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-class ControllerGenerator < Rails::Generator::NamedBase
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper", "#{class_name}HelperTest"
-
- # Controller, helper, views, and test directories.
- m.directory File.join('app/controllers', class_path)
- m.directory File.join('app/helpers', class_path)
- m.directory File.join('app/views', class_path, file_name)
- m.directory File.join('test/functional', class_path)
- m.directory File.join('test/unit/helpers', class_path)
-
- # Controller class, functional test, and helper class.
- m.template 'controller.rb',
- File.join('app/controllers',
- class_path,
- "#{file_name}_controller.rb")
-
- m.template 'functional_test.rb',
- File.join('test/functional',
- class_path,
- "#{file_name}_controller_test.rb")
-
- m.template 'helper.rb',
- File.join('app/helpers',
- class_path,
- "#{file_name}_helper.rb")
-
- m.template 'helper_test.rb',
- File.join('test/unit/helpers',
- class_path,
- "#{file_name}_helper_test.rb")
-
- # View template for each action.
- actions.each do |action|
- path = File.join('app/views', class_path, file_name, "#{action}.html.erb")
- m.template 'view.html.erb', path,
- :assigns => { :action => action, :path => path }
- end
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb b/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb
deleted file mode 100644
index ad85431f98..0000000000
--- a/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-<h1><%= class_name %>#<%= action %></h1>
-<p>Find me in <%= path %></p>
diff --git a/railties/lib/rails_generator/generators/components/helper/USAGE b/railties/lib/rails_generator/generators/components/helper/USAGE
deleted file mode 100644
index ef27ca617e..0000000000
--- a/railties/lib/rails_generator/generators/components/helper/USAGE
+++ /dev/null
@@ -1,24 +0,0 @@
-Description:
- Stubs out a new helper. Pass the helper name, either
- CamelCased or under_scored.
-
- To create a helper within a module, specify the helper name as a
- path like 'parent_module/helper_name'.
-
- This generates a helper class in app/helpers and a helper test
- suite in test/unit/helpers.
-
-Example:
- `./script/generate helper CreditCard`
-
- Credit card helper.
- Helper: app/helpers/credit_card_helper.rb
- Test: test/unit/helpers/credit_card_helper_test.rb
-
-Modules Example:
- `./script/generate helper 'admin/credit_card'`
-
- Credit card admin helper.
- Helper: app/helpers/admin/credit_card_helper.rb
- Test: test/unit/helpers/admin/credit_card_helper_test.rb
-
diff --git a/railties/lib/rails_generator/generators/components/helper/helper_generator.rb b/railties/lib/rails_generator/generators/components/helper/helper_generator.rb
deleted file mode 100644
index f7831f7c7a..0000000000
--- a/railties/lib/rails_generator/generators/components/helper/helper_generator.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-class HelperGenerator < Rails::Generator::NamedBase
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions class_path, "#{class_name}Helper", "#{class_name}HelperTest"
-
- # Helper and helper test directories.
- m.directory File.join('app/helpers', class_path)
- m.directory File.join('test/unit/helpers', class_path)
-
- # Helper and helper test class.
-
- m.template 'helper.rb',
- File.join('app/helpers',
- class_path,
- "#{file_name}_helper.rb")
-
- m.template 'helper_test.rb',
- File.join('test/unit/helpers',
- class_path,
- "#{file_name}_helper_test.rb")
-
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/helper/templates/helper.rb b/railties/lib/rails_generator/generators/components/helper/templates/helper.rb
deleted file mode 100644
index 3fe2ecdc74..0000000000
--- a/railties/lib/rails_generator/generators/components/helper/templates/helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module <%= class_name %>Helper
-end
diff --git a/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb
deleted file mode 100644
index 591e40900e..0000000000
--- a/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class <%= class_name %>HelperTest < ActionView::TestCase
-end
diff --git a/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb b/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb
deleted file mode 100644
index 44323f28ca..0000000000
--- a/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class IntegrationTestGenerator < Rails::Generator::NamedBase
- default_options :skip_migration => false
-
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions class_name, "#{class_name}Test"
-
- # integration test directory
- m.directory File.join('test/integration', class_path)
-
- # integration test stub
- m.template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb")
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb b/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb
deleted file mode 100644
index ba6d60cac6..0000000000
--- a/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-class MailerGenerator < Rails::Generator::NamedBase
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions class_name, "#{class_name}Test"
-
- # Mailer, view, test, and fixture directories.
- m.directory File.join('app/models', class_path)
- m.directory File.join('app/views', file_path)
- m.directory File.join('test/unit', class_path)
- m.directory File.join('test/fixtures', file_path)
-
- # Mailer class and unit test.
- m.template "mailer.rb", File.join('app/models', class_path, "#{file_name}.rb")
- m.template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb")
-
- # View template and fixture for each action.
- actions.each do |action|
- relative_path = File.join(file_path, action)
- view_path = File.join('app/views', "#{relative_path}.erb")
- fixture_path = File.join('test/fixtures', relative_path)
-
- m.template "view.erb", view_path,
- :assigns => { :action => action, :path => view_path }
- m.template "fixture.erb", fixture_path,
- :assigns => { :action => action, :path => view_path }
- end
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb b/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb
deleted file mode 100644
index 6899257ddc..0000000000
--- a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= class_name %>#<%= action %>
-
-Find me in <%= path %>
diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/view.erb b/railties/lib/rails_generator/generators/components/mailer/templates/view.erb
deleted file mode 100644
index 6899257ddc..0000000000
--- a/railties/lib/rails_generator/generators/components/mailer/templates/view.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= class_name %>#<%= action %>
-
-Find me in <%= path %>
diff --git a/railties/lib/rails_generator/generators/components/metal/metal_generator.rb b/railties/lib/rails_generator/generators/components/metal/metal_generator.rb
deleted file mode 100644
index 64f49d929d..0000000000
--- a/railties/lib/rails_generator/generators/components/metal/metal_generator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class MetalGenerator < Rails::Generator::NamedBase
- def manifest
- record do |m|
- m.directory 'app/metal'
- m.template 'metal.rb', File.join('app/metal', "#{file_name}.rb")
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/migration/migration_generator.rb b/railties/lib/rails_generator/generators/components/migration/migration_generator.rb
deleted file mode 100644
index acf41e07df..0000000000
--- a/railties/lib/rails_generator/generators/components/migration/migration_generator.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class MigrationGenerator < Rails::Generator::NamedBase
- def manifest
- record do |m|
- m.migration_template 'migration.rb', 'db/migrate', :assigns => get_local_assigns
- end
- end
-
-
- private
- def get_local_assigns
- returning(assigns = {}) do
- if class_name.underscore =~ /^(add|remove)_.*_(?:to|from)_(.*)/
- assigns[:migration_action] = $1
- assigns[:table_name] = $2.pluralize
- else
- assigns[:attributes] = []
- end
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/model/model_generator.rb b/railties/lib/rails_generator/generators/components/model/model_generator.rb
deleted file mode 100644
index 582a28922f..0000000000
--- a/railties/lib/rails_generator/generators/components/model/model_generator.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-class ModelGenerator < Rails::Generator::NamedBase
- default_options :skip_timestamps => false, :skip_migration => false, :skip_fixture => false
-
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions class_name, "#{class_name}Test"
-
- # Model, test, and fixture directories.
- m.directory File.join('app/models', class_path)
- m.directory File.join('test/unit', class_path)
- m.directory File.join('test/fixtures', class_path)
-
- # Model class, unit test, and fixtures.
- m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
- m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
-
- unless options[:skip_fixture]
- m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml")
- end
-
- unless options[:skip_migration]
- m.migration_template 'migration.rb', 'db/migrate', :assigns => {
- :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
- }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
- end
- end
- end
-
- protected
- def banner
- "Usage: #{$0} #{spec.name} ModelName [field:type, field:type]"
- end
-
- def add_options!(opt)
- opt.separator ''
- opt.separator 'Options:'
- opt.on("--skip-timestamps",
- "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
- opt.on("--skip-migration",
- "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
- opt.on("--skip-fixture",
- "Don't generation a fixture file for this model") { |v| options[:skip_fixture] = v}
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/USAGE b/railties/lib/rails_generator/generators/components/model_subclass/USAGE
deleted file mode 100644
index a4b558a401..0000000000
--- a/railties/lib/rails_generator/generators/components/model_subclass/USAGE
+++ /dev/null
@@ -1,13 +0,0 @@
-Description:
- Create a model subclass of parent, used for Single Table Inheritance.
-
- Both subclass and parent name can be either CamelCased or under_scored.
-
- This generates a model class in app/models and a unit test in test/unit.
-
-Examples:
- `./script/generate model_subclass admin user`
-
- creates an Admin model, which will inheritate from User model, test:
- Model: app/models/admin.rb
- Test: test/unit/admin_test.rb
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb b/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb
deleted file mode 100644
index e8ac3da2cd..0000000000
--- a/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class ModelSubclassGenerator < Rails::Generator::NamedBase
- default_options :skip_unit_test => false
-
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions class_name, "#{class_name}Test"
-
- # Model and test directories.
- m.directory File.join('app/models', class_path)
- m.directory File.join('test/unit', class_path)
-
- # Model class and unit test
- m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb"), :assigns => assigns
- m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb"), :assigns => assigns
-
- end
- end
-
- protected
- def banner
- "Usage: #{$0} #{spec.name} Subclass Parent"
- end
-
- def assigns
- {:parent_class_name => parent_class_name}
- end
-
- def parent_class_name
- @args.first.try(:camelize) || usage
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb b/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb
deleted file mode 100644
index d0037b322b..0000000000
--- a/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class <%= class_name %> < <%= parent_class_name %>
-
-end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/components/observer/observer_generator.rb b/railties/lib/rails_generator/generators/components/observer/observer_generator.rb
deleted file mode 100644
index 3c4b330a80..0000000000
--- a/railties/lib/rails_generator/generators/components/observer/observer_generator.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class ObserverGenerator < Rails::Generator::NamedBase
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions "#{class_name}Observer", "#{class_name}ObserverTest"
-
- # Observer, and test directories.
- m.directory File.join('app/models', class_path)
- m.directory File.join('test/unit', class_path)
-
- # Observer class and unit test fixtures.
- m.template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb")
- m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb")
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb b/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
deleted file mode 100644
index 83ce8ac674..0000000000
--- a/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class PerformanceTestGenerator < Rails::Generator::NamedBase
- default_options :skip_migration => false
-
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions class_name, "#{class_name}Test"
-
- # performance test directory
- m.directory File.join('test/performance', class_path)
-
- # performance test stub
- m.template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb")
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/plugin/USAGE b/railties/lib/rails_generator/generators/components/plugin/USAGE
deleted file mode 100644
index d2ecfc2d59..0000000000
--- a/railties/lib/rails_generator/generators/components/plugin/USAGE
+++ /dev/null
@@ -1,25 +0,0 @@
-Description:
- Stubs out a new plugin. Pass the plugin name, either CamelCased or
- under_scored, as an argument. Pass --with-generator to add an example
- generator also.
-
- This creates a plugin in vendor/plugins including an init.rb and README
- as well as standard lib, task, and test directories.
-
-Example:
- `./script/generate plugin BrowserFilters`
-
- creates a standard browser_filters plugin:
- vendor/plugins/browser_filters/README
- vendor/plugins/browser_filters/init.rb
- vendor/plugins/browser_filters/install.rb
- vendor/plugins/browser_filters/lib/browser_filters.rb
- vendor/plugins/browser_filters/test/browser_filters_test.rb
- vendor/plugins/browser_filters/tasks/browser_filters_tasks.rake
-
- ./script/generate plugin BrowserFilters --with-generator
-
- creates a browser_filters generator also:
- vendor/plugins/browser_filters/generators/browser_filters/browser_filters_generator.rb
- vendor/plugins/browser_filters/generators/browser_filters/USAGE
- vendor/plugins/browser_filters/generators/browser_filters/templates/
diff --git a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb b/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
deleted file mode 100644
index 6826998252..0000000000
--- a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-class PluginGenerator < Rails::Generator::NamedBase
- attr_reader :plugin_path
-
- def initialize(runtime_args, runtime_options = {})
- @with_generator = runtime_args.delete("--with-generator")
- super
- @plugin_path = "vendor/plugins/#{file_name}"
- end
-
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions class_name
-
- m.directory "#{plugin_path}/lib"
- m.directory "#{plugin_path}/tasks"
- m.directory "#{plugin_path}/test"
-
- m.template 'README', "#{plugin_path}/README"
- m.template 'MIT-LICENSE', "#{plugin_path}/MIT-LICENSE"
- m.template 'Rakefile', "#{plugin_path}/Rakefile"
- m.template 'init.rb', "#{plugin_path}/init.rb"
- m.template 'install.rb', "#{plugin_path}/install.rb"
- m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb"
- m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb"
- m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake"
- m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb"
- m.template 'test_helper.rb', "#{plugin_path}/test/test_helper.rb"
- if @with_generator
- m.directory "#{plugin_path}/generators"
- m.directory "#{plugin_path}/generators/#{file_name}"
- m.directory "#{plugin_path}/generators/#{file_name}/templates"
-
- m.template 'generator.rb', "#{plugin_path}/generators/#{file_name}/#{file_name}_generator.rb"
- m.template 'USAGE', "#{plugin_path}/generators/#{file_name}/USAGE"
- end
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb b/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb
deleted file mode 100644
index 3e800df6c5..0000000000
--- a/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class <%= class_name %>Generator < Rails::Generator::NamedBase
- def manifest
- record do |m|
- # m.directory "lib"
- # m.template 'README', "README"
- end
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb b/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb
deleted file mode 100644
index 3e0bc29d3a..0000000000
--- a/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require 'test_helper'
-
-class <%= class_name %>Test < ActiveSupport::TestCase
- # Replace this with your real tests.
- test "the truth" do
- assert true
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/resource/resource_generator.rb b/railties/lib/rails_generator/generators/components/resource/resource_generator.rb
deleted file mode 100644
index 4ee2fbff63..0000000000
--- a/railties/lib/rails_generator/generators/components/resource/resource_generator.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-class ResourceGenerator < Rails::Generator::NamedBase
- default_options :skip_timestamps => false, :skip_migration => false
-
- attr_reader :controller_name,
- :controller_class_path,
- :controller_file_path,
- :controller_class_nesting,
- :controller_class_nesting_depth,
- :controller_class_name,
- :controller_singular_name,
- :controller_plural_name
- alias_method :controller_file_name, :controller_singular_name
- alias_method :controller_table_name, :controller_plural_name
-
- def initialize(runtime_args, runtime_options = {})
- super
-
- @controller_name = @name.pluralize
-
- base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
- @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
-
- if @controller_class_nesting.empty?
- @controller_class_name = @controller_class_name_without_nesting
- else
- @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
- end
- end
-
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
- m.class_collisions(class_name)
-
- # Controller, helper, views, and test directories.
- m.directory(File.join('app/models', class_path))
- m.directory(File.join('app/controllers', controller_class_path))
- m.directory(File.join('app/helpers', controller_class_path))
- m.directory(File.join('app/views', controller_class_path, controller_file_name))
- m.directory(File.join('test/functional', controller_class_path))
- m.directory(File.join('test/unit', class_path))
- m.directory(File.join('test/unit/helpers', class_path))
-
- m.dependency 'model', [name] + @args, :collision => :skip
-
- m.template(
- 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
- )
-
- m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
- m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
- m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb"))
-
- m.route_resources controller_file_name
- end
- end
-
- protected
- def banner
- "Usage: #{$0} resource ModelName [field:type, field:type]"
- end
-
- def add_options!(opt)
- opt.separator ''
- opt.separator 'Options:'
- opt.on("--skip-timestamps",
- "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
- opt.on("--skip-migration",
- "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
- end
-
- def model_name
- class_name.demodulize
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/resource/templates/controller.rb b/railties/lib/rails_generator/generators/components/resource/templates/controller.rb
deleted file mode 100644
index 765a942694..0000000000
--- a/railties/lib/rails_generator/generators/components/resource/templates/controller.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class <%= controller_class_name %>Controller < ApplicationController
-end
diff --git a/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb b/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb
deleted file mode 100644
index b1bb1dacbf..0000000000
--- a/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require 'test_helper'
-
-class <%= controller_class_name %>ControllerTest < ActionController::TestCase
- # Replace this with your real tests.
- test "the truth" do
- assert true
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/resource/templates/helper.rb b/railties/lib/rails_generator/generators/components/resource/templates/helper.rb
deleted file mode 100644
index 9bd821b1b2..0000000000
--- a/railties/lib/rails_generator/generators/components/resource/templates/helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module <%= controller_class_name %>Helper
-end
diff --git a/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb
deleted file mode 100644
index 061f64a5e3..0000000000
--- a/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class <%= controller_class_name %>HelperTest < ActionView::TestCase
-end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
deleted file mode 100644
index 2a5edeedb6..0000000000
--- a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-class ScaffoldGenerator < Rails::Generator::NamedBase
- default_options :skip_timestamps => false, :skip_migration => false, :force_plural => false
-
- attr_reader :controller_name,
- :controller_class_path,
- :controller_file_path,
- :controller_class_nesting,
- :controller_class_nesting_depth,
- :controller_class_name,
- :controller_underscore_name,
- :controller_singular_name,
- :controller_plural_name
- alias_method :controller_file_name, :controller_underscore_name
- alias_method :controller_table_name, :controller_plural_name
-
- def initialize(runtime_args, runtime_options = {})
- super
-
- if @name == @name.pluralize && !options[:force_plural]
- logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural."
- @name = @name.singularize
- end
-
- @controller_name = @name.pluralize
-
- base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
- @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
- @controller_singular_name=base_name.singularize
- if @controller_class_nesting.empty?
- @controller_class_name = @controller_class_name_without_nesting
- else
- @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
- end
- end
-
- def manifest
- record do |m|
- # Check for class naming collisions.
- m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
- m.class_collisions(class_name)
-
- # Controller, helper, views, test and stylesheets directories.
- m.directory(File.join('app/models', class_path))
- m.directory(File.join('app/controllers', controller_class_path))
- m.directory(File.join('app/helpers', controller_class_path))
- m.directory(File.join('app/views', controller_class_path, controller_file_name))
- m.directory(File.join('app/views/layouts', controller_class_path))
- m.directory(File.join('test/functional', controller_class_path))
- m.directory(File.join('test/unit', class_path))
- m.directory(File.join('test/unit/helpers', class_path))
- m.directory(File.join('public/stylesheets', class_path))
-
- for action in scaffold_views
- m.template(
- "view_#{action}.html.erb",
- File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb")
- )
- end
-
- # Layout and stylesheet.
- m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb"))
- m.template('style.css', 'public/stylesheets/scaffold.css')
-
- m.template(
- 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
- )
-
- m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
- m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
- m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb"))
-
- m.route_resources controller_file_name
-
- m.dependency 'model', [name] + @args, :collision => :skip
- end
- end
-
- protected
- # Override with your own usage banner.
- def banner
- "Usage: #{$0} scaffold ModelName [field:type, field:type]"
- end
-
- def add_options!(opt)
- opt.separator ''
- opt.separator 'Options:'
- opt.on("--skip-timestamps",
- "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
- opt.on("--skip-migration",
- "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
- opt.on("--force-plural",
- "Forces the generation of a plural ModelName") { |v| options[:force_plural] = v }
- end
-
- def scaffold_views
- %w[ index show new edit ]
- end
-
- def model_name
- class_name.demodulize
- end
-end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb b/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb
deleted file mode 100644
index 9bd821b1b2..0000000000
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module <%= controller_class_name %>Helper
-end
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb
deleted file mode 100644
index 061f64a5e3..0000000000
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class <%= controller_class_name %>HelperTest < ActionView::TestCase
-end
diff --git a/railties/lib/rails_generator/generators/components/session_migration/USAGE b/railties/lib/rails_generator/generators/components/session_migration/USAGE
deleted file mode 100644
index 87117a3cb6..0000000000
--- a/railties/lib/rails_generator/generators/components/session_migration/USAGE
+++ /dev/null
@@ -1,10 +0,0 @@
-Description:
- Creates a migration to add the sessions table used by the Active Record
- session store. Pass the migration name, either CamelCased or under_scored,
- as an argument.
-
-Example:
- `./script/generate session_migration CreateSessionTable`
-
- With 4 existing migrations, this creates the AddSessionTable migration
- in db/migrate/005_add_session_table.rb
diff --git a/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb b/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb
deleted file mode 100644
index 2e177033a1..0000000000
--- a/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-class SessionMigrationGenerator < Rails::Generator::NamedBase
- def initialize(runtime_args, runtime_options = {})
- runtime_args << 'add_session_table' if runtime_args.empty?
- super
- end
-
- def manifest
- record do |m|
- m.migration_template 'migration.rb', 'db/migrate',
- :assigns => { :session_table_name => default_session_table_name }
- end
- end
-
- protected
- def default_session_table_name
- ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session'
- end
-end
diff --git a/railties/lib/rails_generator/lookup.rb b/railties/lib/rails_generator/lookup.rb
deleted file mode 100644
index a3525364a2..0000000000
--- a/railties/lib/rails_generator/lookup.rb
+++ /dev/null
@@ -1,249 +0,0 @@
-require 'pathname'
-
-require File.dirname(__FILE__) + '/spec'
-
-class Object
- class << self
- # Lookup missing generators using const_missing. This allows any
- # generator to reference another without having to know its location:
- # RubyGems, ~/.rails/generators, and RAILS_ROOT/generators.
- def lookup_missing_generator(class_id)
- if md = /(.+)Generator$/.match(class_id.to_s)
- name = md.captures.first.demodulize.underscore
- Rails::Generator::Base.lookup(name).klass
- else
- const_missing_before_generators(class_id)
- end
- end
-
- unless respond_to?(:const_missing_before_generators)
- alias_method :const_missing_before_generators, :const_missing
- alias_method :const_missing, :lookup_missing_generator
- end
- end
-end
-
-# User home directory lookup adapted from RubyGems.
-def Dir.user_home
- if ENV['HOME']
- ENV['HOME']
- elsif ENV['USERPROFILE']
- ENV['USERPROFILE']
- elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
- "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
- else
- File.expand_path '~'
- end
-end
-
-
-module Rails
- module Generator
-
- # Generator lookup is managed by a list of sources which return specs
- # describing where to find and how to create generators. This module
- # provides class methods for manipulating the source list and looking up
- # generator specs, and an #instance wrapper for quickly instantiating
- # generators by name.
- #
- # A spec is not a generator: it's a description of where to find
- # the generator and how to create it. A source is anything that
- # yields generators from #each. PathSource and GemGeneratorSource are provided.
- module Lookup
- def self.included(base)
- base.extend(ClassMethods)
- base.use_component_sources!
- end
-
- # Convenience method to instantiate another generator.
- def instance(generator_name, args, runtime_options = {})
- self.class.instance(generator_name, args, runtime_options)
- end
-
- module ClassMethods
- # The list of sources where we look, in order, for generators.
- def sources
- read_inheritable_attribute(:sources) or use_component_sources!
- end
-
- # Add a source to the end of the list.
- def append_sources(*args)
- sources.concat(args.flatten)
- invalidate_cache!
- end
-
- # Add a source to the beginning of the list.
- def prepend_sources(*args)
- write_inheritable_array(:sources, args.flatten + sources)
- invalidate_cache!
- end
-
- # Reset the source list.
- def reset_sources
- write_inheritable_attribute(:sources, [])
- invalidate_cache!
- end
-
- # Use application generators (app, ?).
- def use_application_sources!
- reset_sources
- sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
- end
-
- # Use component generators (model, controller, etc).
- # 1. Rails application. If RAILS_ROOT is defined we know we're
- # generating in the context of a Rails application, so search
- # RAILS_ROOT/generators.
- # 2. Look in plugins, either for generators/ or rails_generators/
- # directories within each plugin
- # 3. User home directory. Search ~/.rails/generators.
- # 4. RubyGems. Search for gems named *_generator, and look for
- # generators within any RubyGem's
- # /rails_generators/<generator_name>_generator.rb file.
- # 5. Builtins. Model, controller, mailer, scaffold, and so on.
- def use_component_sources!
- reset_sources
- if defined? ::RAILS_ROOT
- sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators")
- sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators")
- Rails.configuration.plugin_paths.each do |path|
- relative_path = Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(::RAILS_ROOT))
- sources << PathSource.new(:"plugins (#{relative_path})", "#{path}/*/**/{,rails_}generators")
- end
- end
- sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
- if Object.const_defined?(:Gem)
- sources << GemGeneratorSource.new
- sources << GemPathSource.new
- end
- sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
- end
-
- # Lookup knows how to find generators' Specs from a list of Sources.
- # Searches the sources, in order, for the first matching name.
- def lookup(generator_name)
- @found ||= {}
- generator_name = generator_name.to_s.downcase
- @found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
- unless @found[generator_name]
- chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
- rx = /^#{chars}$/
- gns = cache.select{|spec| spec.name =~ rx }
- @found[generator_name] ||= gns.first if gns.length == 1
- raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
- end
- @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
- end
-
- # Convenience method to lookup and instantiate a generator.
- def instance(generator_name, args = [], runtime_options = {})
- lookup(generator_name).klass.new(args, full_options(runtime_options))
- end
-
- private
- # Lookup and cache every generator from the source list.
- def cache
- @cache ||= sources.inject([]) { |cache, source| cache + source.to_a }
- end
-
- # Clear the cache whenever the source list changes.
- def invalidate_cache!
- @cache = nil
- end
- end
- end
-
- # Sources enumerate (yield from #each) generator specs which describe
- # where to find and how to create generators. Enumerable is mixed in so,
- # for example, source.collect will retrieve every generator.
- # Sources may be assigned a label to distinguish them.
- class Source
- include Enumerable
-
- attr_reader :label
- def initialize(label)
- @label = label
- end
-
- # The each method must be implemented in subclasses.
- # The base implementation raises an error.
- def each
- raise NotImplementedError
- end
-
- # Return a convenient sorted list of all generator names.
- def names
- map { |spec| spec.name }.sort
- end
- end
-
-
- # PathSource looks for generators in a filesystem directory.
- class PathSource < Source
- attr_reader :path
-
- def initialize(label, path)
- super label
- @path = path
- end
-
- # Yield each eligible subdirectory.
- def each
- Dir["#{path}/[a-z]*"].each do |dir|
- if File.directory?(dir)
- yield Spec.new(File.basename(dir), dir, label)
- end
- end
- end
- end
-
- class AbstractGemSource < Source
- def initialize
- super :RubyGems
- end
- end
-
- # GemGeneratorSource hits the mines to quarry for generators. The latest versions
- # of gems named *_generator are selected.
- class GemGeneratorSource < AbstractGemSource
- # Yield latest versions of generator gems.
- def each
- dependency = Gem::Dependency.new(/_generator$/, Gem::Requirement.default)
- Gem::cache.search(dependency).inject({}) { |latest, gem|
- hem = latest[gem.name]
- latest[gem.name] = gem if hem.nil? or gem.version > hem.version
- latest
- }.values.each { |gem|
- yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
- }
- end
- end
-
- # GemPathSource looks for generators within any RubyGem's /rails_generators/<generator_name>_generator.rb file.
- class GemPathSource < AbstractGemSource
- # Yield each generator within rails_generator subdirectories.
- def each
- generator_full_paths.each do |generator|
- yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label)
- end
- end
-
- private
- def generator_full_paths
- @generator_full_paths ||=
- Gem::cache.inject({}) do |latest, name_gem|
- name, gem = name_gem
- hem = latest[gem.name]
- latest[gem.name] = gem if hem.nil? or gem.version > hem.version
- latest
- end.values.inject([]) do |mem, gem|
- Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator|
- mem << generator
- end
- mem
- end
- end
- end
-
- end
-end
diff --git a/railties/lib/rails_generator/manifest.rb b/railties/lib/rails_generator/manifest.rb
deleted file mode 100644
index 702effa76f..0000000000
--- a/railties/lib/rails_generator/manifest.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-module Rails
- module Generator
-
- # Manifest captures the actions a generator performs. Instantiate
- # a manifest with an optional target object, hammer it with actions,
- # then replay or rewind on the object of your choice.
- #
- # Example:
- # manifest = Manifest.new { |m|
- # m.make_directory '/foo'
- # m.create_file '/foo/bar.txt'
- # }
- # manifest.replay(creator)
- # manifest.rewind(destroyer)
- class Manifest
- attr_reader :target
-
- # Take a default action target. Yield self if block given.
- def initialize(target = nil)
- @target, @actions = target, []
- yield self if block_given?
- end
-
- # Record an action.
- def method_missing(action, *args, &block)
- @actions << [action, args, block]
- end
-
- # Replay recorded actions.
- def replay(target = nil)
- send_actions(target || @target, @actions)
- end
-
- # Rewind recorded actions.
- def rewind(target = nil)
- send_actions(target || @target, @actions.reverse)
- end
-
- # Erase recorded actions.
- def erase
- @actions = []
- end
-
- private
- def send_actions(target, actions)
- actions.each do |method, args, block|
- target.send(method, *args, &block)
- end
- end
- end
-
- end
-end
diff --git a/railties/lib/rails_generator/options.rb b/railties/lib/rails_generator/options.rb
deleted file mode 100644
index 5f6aefa921..0000000000
--- a/railties/lib/rails_generator/options.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-require 'optparse'
-
-module Rails
- module Generator
- module Options
- def self.included(base)
- base.extend(ClassMethods)
- class << base
- if respond_to?(:inherited)
- alias_method :inherited_without_options, :inherited
- end
- alias_method :inherited, :inherited_with_options
- end
- end
-
- module ClassMethods
- def inherited_with_options(sub)
- inherited_without_options(sub) if respond_to?(:inherited_without_options)
- sub.extend(Rails::Generator::Options::ClassMethods)
- end
-
- def mandatory_options(options = nil)
- if options
- write_inheritable_attribute(:mandatory_options, options)
- else
- read_inheritable_attribute(:mandatory_options) or write_inheritable_attribute(:mandatory_options, {})
- end
- end
-
- def default_options(options = nil)
- if options
- write_inheritable_attribute(:default_options, options)
- else
- read_inheritable_attribute(:default_options) or write_inheritable_attribute(:default_options, {})
- end
- end
-
- # Merge together our class options. In increasing precedence:
- # default_options (class default options)
- # runtime_options (provided as argument)
- # mandatory_options (class mandatory options)
- def full_options(runtime_options = {})
- default_options.merge(runtime_options).merge(mandatory_options)
- end
-
- end
-
- # Each instance has an options hash that's populated by #parse.
- def options
- @options ||= {}
- end
- attr_writer :options
-
- protected
- # Convenient access to class mandatory options.
- def mandatory_options
- self.class.mandatory_options
- end
-
- # Convenient access to class default options.
- def default_options
- self.class.default_options
- end
-
- # Merge together our instance options. In increasing precedence:
- # default_options (class default options)
- # options (instance options)
- # runtime_options (provided as argument)
- # mandatory_options (class mandatory options)
- def full_options(runtime_options = {})
- self.class.full_options(options.merge(runtime_options))
- end
-
- # Parse arguments into the options hash. Classes may customize
- # parsing behavior by overriding these methods:
- # #banner Usage: ./script/generate [options]
- # #add_options! Options:
- # some options..
- # #add_general_options! General Options:
- # general options..
- def parse!(args, runtime_options = {})
- self.options = {}
-
- @option_parser = OptionParser.new do |opt|
- opt.banner = banner
- add_options!(opt)
- add_general_options!(opt)
- opt.parse!(args)
- end
-
- return args
- ensure
- self.options = full_options(runtime_options)
- end
-
- # Raise a usage error. Override usage_message to provide a blurb
- # after the option parser summary.
- def usage(message = usage_message)
- raise UsageError, "#{@option_parser}\n#{message}"
- end
-
- def usage_message
- ''
- end
-
- # Override with your own usage banner.
- def banner
- "Usage: #{$0} [options]"
- end
-
- # Override to add your options to the parser:
- # def add_options!(opt)
- # opt.on('-v', '--verbose') { |value| options[:verbose] = value }
- # end
- def add_options!(opt)
- end
-
- # Adds general options like -h and --quiet. Usually don't override.
- def add_general_options!(opt)
- opt.separator ''
- opt.separator 'Rails Info:'
- opt.on('-v', '--version', 'Show the Rails version number and quit.')
- opt.on('-h', '--help', 'Show this help message and quit.') { |v| options[:help] = v }
-
- opt.separator ''
- opt.separator 'General Options:'
-
- opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v }
- opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force }
- opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip }
- opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v }
- opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v }
- opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do
- options[:svn] = `svn status`.inject({}) do |opt, e|
- opt[e.chomp[7..-1]] = true
- opt
- end
- end
- opt.on('-g', '--git', 'Modify files with git. (Note: git must be in path)') do
- options[:git] = `git status`.inject({:new => {}, :modified => {}}) do |opt, e|
- opt[:new][e.chomp[14..-1]] = true if e =~ /new file:/
- opt[:modified][e.chomp[14..-1]] = true if e =~ /modified:/
- opt
- end
- end
- end
-
- end
- end
-end
diff --git a/railties/lib/rails_generator/scripts.rb b/railties/lib/rails_generator/scripts.rb
deleted file mode 100644
index 3763b75dba..0000000000
--- a/railties/lib/rails_generator/scripts.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require File.dirname(__FILE__) + '/options'
-
-module Rails
- module Generator
- module Scripts
-
- # Generator scripts handle command-line invocation. Each script
- # responds to an invoke! class method which handles option parsing
- # and generator invocation.
- class Base
- include Options
- default_options :collision => :ask, :quiet => false
-
- # Run the generator script. Takes an array of unparsed arguments
- # and a hash of parsed arguments, takes the generator as an option
- # or first remaining argument, and invokes the requested command.
- def run(args = [], runtime_options = {})
- begin
- parse!(args.dup, runtime_options)
- rescue OptionParser::InvalidOption => e
- # Don't cry, script. Generators want what you think is invalid.
- end
-
- # Generator name is the only required option.
- unless options[:generator]
- usage if args.empty?
- options[:generator] ||= args.shift
- end
-
- # Look up generator instance and invoke command on it.
- Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
- rescue => e
- puts e
- puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace]
- raise SystemExit
- end
-
- protected
- # Override with your own script usage banner.
- def banner
- "Usage: #{$0} generator [options] [args]"
- end
-
- def usage_message
- usage = "\nInstalled Generators\n"
- Rails::Generator::Base.sources.inject([]) do |mem, source|
- # Using an association list instead of a hash to preserve order,
- # for aesthetic reasons more than anything else.
- label = source.label.to_s.capitalize
- pair = mem.assoc(label)
- mem << (pair = [label, []]) if pair.nil?
- pair[1] |= source.names
- mem
- end.each do |label, names|
- usage << " #{label}: #{names.join(', ')}\n" unless names.empty?
- end
-
- usage << <<end_blurb
-
-You can also install additional generators for your own use:
- 1. Download, for example, login_generator.zip
- 2. Unzip to directory #{Dir.user_home}/.rails/generators/login
- to use the generator with all your Rails apps
-end_blurb
-
- if Object.const_defined?(:RAILS_ROOT)
- usage << <<end_blurb
- or to #{File.expand_path(RAILS_ROOT)}/lib/generators/login
- to use with this app only.
-end_blurb
- end
-
- usage << <<end_blurb
- 3. Run generate with no arguments for usage information
- #{$0} login
-
-Generator gems are also available:
- 1. gem search -r generator
- 2. gem install login_generator
- 3. #{$0} login
-
-end_blurb
- return usage
- end
- end # Base
-
- end
- end
-end
diff --git a/railties/lib/rails_generator/scripts/destroy.rb b/railties/lib/rails_generator/scripts/destroy.rb
deleted file mode 100644
index a7c2a14751..0000000000
--- a/railties/lib/rails_generator/scripts/destroy.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require File.dirname(__FILE__) + '/../scripts'
-
-module Rails::Generator::Scripts
- class Destroy < Base
- mandatory_options :command => :destroy
-
- protected
- def usage_message
- usage = "\nInstalled Generators\n"
- Rails::Generator::Base.sources.each do |source|
- label = source.label.to_s.capitalize
- names = source.names
- usage << " #{label}: #{names.join(', ')}\n" unless names.empty?
- end
-
- usage << <<end_blurb
-
-script/generate command. For instance, 'script/destroy migration CreatePost'
-will delete the appropriate XXX_create_post.rb migration file in db/migrate,
-while 'script/destroy scaffold Post' will delete the posts controller and
-views, post model and migration, all associated tests, and the map.resources
-:posts line in config/routes.rb.
-
-For instructions on finding new generators, run script/generate.
-end_blurb
- return usage
- end
- end
-end
diff --git a/railties/lib/rails_generator/scripts/generate.rb b/railties/lib/rails_generator/scripts/generate.rb
deleted file mode 100644
index 1fe2f54ab3..0000000000
--- a/railties/lib/rails_generator/scripts/generate.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require File.dirname(__FILE__) + '/../scripts'
-
-module Rails::Generator::Scripts
- class Generate < Base
- mandatory_options :command => :create
- end
-end
diff --git a/railties/lib/rails_generator/scripts/update.rb b/railties/lib/rails_generator/scripts/update.rb
deleted file mode 100644
index 53a9faa366..0000000000
--- a/railties/lib/rails_generator/scripts/update.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require File.dirname(__FILE__) + '/../scripts'
-
-module Rails::Generator::Scripts
- class Update < Base
- mandatory_options :command => :update
-
- protected
- def banner
- "Usage: #{$0} [options] scaffold"
- end
- end
-end
diff --git a/railties/lib/rails_generator/secret_key_generator.rb b/railties/lib/rails_generator/secret_key_generator.rb
deleted file mode 100644
index 7dd495a2f5..0000000000
--- a/railties/lib/rails_generator/secret_key_generator.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'active_support/deprecation'
-
-module Rails
- # A class for creating random secret keys. This class will do its best to create a
- # random secret key that's as secure as possible, using whatever methods are
- # available on the current platform. For example:
- #
- # generator = Rails::SecretKeyGenerator("some unique identifier, such as the application name")
- # generator.generate_secret # => "f3f1be90053fa851... (some long string)"
- #
- # This class is *deprecated* in Rails 2.2 in favor of ActiveSupport::SecureRandom.
- # It is currently a wrapper around ActiveSupport::SecureRandom.
- class SecretKeyGenerator
- def initialize(identifier)
- end
-
- # Generate a random secret key with the best possible method available on
- # the current platform.
- def generate_secret
- ActiveSupport::SecureRandom.hex(64)
- end
- deprecate :generate_secret=>"You should use ActiveSupport::SecureRandom.hex(64)"
- end
-end
diff --git a/railties/lib/rails_generator/simple_logger.rb b/railties/lib/rails_generator/simple_logger.rb
deleted file mode 100644
index d750f07b84..0000000000
--- a/railties/lib/rails_generator/simple_logger.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-module Rails
- module Generator
- class SimpleLogger # :nodoc:
- attr_reader :out
- attr_accessor :quiet
-
- def initialize(out = $stdout)
- @out = out
- @quiet = false
- @level = 0
- end
-
- def log(status, message, &block)
- @out.print("%12s %s%s\n" % [status, ' ' * @level, message]) unless quiet
- indent(&block) if block_given?
- end
-
- def indent(&block)
- @level += 1
- if block_given?
- begin
- block.call
- ensure
- outdent
- end
- end
- end
-
- def outdent
- @level -= 1
- if block_given?
- begin
- block.call
- ensure
- indent
- end
- end
- end
-
- private
- def method_missing(method, *args, &block)
- log(method.to_s, args.first, &block)
- end
- end
- end
-end
diff --git a/railties/lib/rails_generator/spec.rb b/railties/lib/rails_generator/spec.rb
deleted file mode 100644
index 9d780b7ac5..0000000000
--- a/railties/lib/rails_generator/spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module Rails
- module Generator
- # A spec knows where a generator was found and how to instantiate it.
- # Metadata include the generator's name, its base path, and the source
- # which yielded it (PathSource, GemPathSource, etc.)
- class Spec
- attr_reader :name, :path, :source
-
- def initialize(name, path, source)
- @name, @path, @source = name, path, source
- end
-
- # Look up the generator class. Require its class file, find the class
- # in ObjectSpace, tag it with this spec, and return.
- def klass
- unless @klass
- require class_file
- @klass = lookup_class
- @klass.spec = self
- end
- @klass
- end
-
- def class_file
- "#{path}/#{name}_generator.rb"
- end
-
- def class_name
- "#{name.camelize}Generator"
- end
-
- private
- # Search for the first Class descending from Rails::Generator::Base
- # whose name matches the requested class name.
- def lookup_class
- ObjectSpace.each_object(Class) do |obj|
- return obj if obj.ancestors.include?(Rails::Generator::Base) and
- obj.name.split('::').last == class_name
- end
- raise NameError, "Missing #{class_name} class in #{class_file}"
- end
- end
- end
-end
diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake
index 0e256737f9..0d4d658315 100644
--- a/railties/lib/tasks/databases.rake
+++ b/railties/lib/tasks/databases.rake
@@ -411,9 +411,9 @@ namespace :db do
desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
task :create => :environment do
raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
- require 'rails_generator'
- require 'rails_generator/scripts/generate'
- Rails::Generator::Scripts::Generate.new.run(["session_migration", ENV["MIGRATION"] || "CreateSessions"])
+ require 'generators'
+ require 'generators/rails/session_migration/session_migration_generator'
+ Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ]
end
desc "Clear the sessions table"
diff --git a/railties/lib/tasks/documentation.rake b/railties/lib/tasks/documentation.rake
index 8b41478a93..db1939c45f 100644
--- a/railties/lib/tasks/documentation.rake
+++ b/railties/lib/tasks/documentation.rake
@@ -21,7 +21,7 @@ namespace :doc do
rdoc.rdoc_files.include('vendor/rails/railties/CHANGELOG')
rdoc.rdoc_files.include('vendor/rails/railties/MIT-LICENSE')
rdoc.rdoc_files.include('vendor/rails/railties/README')
- rdoc.rdoc_files.include('vendor/rails/railties/lib/{*.rb,commands/*.rb,rails_generator/*.rb}')
+ rdoc.rdoc_files.include('vendor/rails/railties/lib/{*.rb,commands/*.rb,generators/*.rb}')
rdoc.rdoc_files.include('vendor/rails/activerecord/README')
rdoc.rdoc_files.include('vendor/rails/activerecord/CHANGELOG')
rdoc.rdoc_files.include('vendor/rails/activerecord/lib/active_record/**/*.rb')
diff --git a/railties/lib/tasks/framework.rake b/railties/lib/tasks/framework.rake
index b6f2f2bc18..a084af7e11 100644
--- a/railties/lib/tasks/framework.rake
+++ b/railties/lib/tasks/framework.rake
@@ -78,51 +78,48 @@ namespace :rails do
end
desc "Update both configs, scripts and public/javascripts from Rails"
- task :update => [ "update:scripts", "update:javascripts", "update:configs", "update:application_controller" ]
+ task :update => [ "update:configs", "update:javascripts", "update:scripts", "update:application_controller" ]
desc "Applies the template supplied by LOCATION=/path/to/template"
task :template do
- require 'rails_generator/generators/applications/app/template_runner'
template = ENV["LOCATION"]
template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://}
- Rails::TemplateRunner.new(template)
+
+ require 'generators'
+ generator = Rails::Generators::App.new [ RAILS_ROOT ], {}, :destination_root => RAILS_ROOT
+ generator.apply template, :verbose => false
end
namespace :update do
- desc "Add new scripts to the application script/ directory"
- task :scripts do
- local_base = "script"
- edge_base = "#{File.dirname(__FILE__)}/../../bin"
-
- local = Dir["#{local_base}/**/*"].reject { |path| File.directory?(path) }
- edge = Dir["#{edge_base}/**/*"].reject { |path| File.directory?(path) }
-
- edge.each do |script|
- base_name = script[(edge_base.length+1)..-1]
- next if base_name == "rails"
- next if local.detect { |path| base_name == path[(local_base.length+1)..-1] }
- if !File.directory?("#{local_base}/#{File.dirname(base_name)}")
- mkdir_p "#{local_base}/#{File.dirname(base_name)}"
- end
- install script, "#{local_base}/#{base_name}", :mode => 0755
- end
- end
+ def invoke_from_app_generator(method)
+ require 'generators'
+ require 'generators/rails/app/app_generator'
- desc "Update your javascripts from your current rails install"
- task :javascripts do
- require 'railties_path'
- project_dir = RAILS_ROOT + '/public/javascripts/'
- scripts = Dir[RAILTIES_PATH + '/html/javascripts/*.js']
- scripts.reject!{|s| File.basename(s) == 'application.js'} if File.exist?(project_dir + 'application.js')
- FileUtils.cp(scripts, project_dir)
+ generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true },
+ :destination_root => RAILS_ROOT
+ generator.invoke(method)
end
desc "Update config/boot.rb from your current rails install"
task :configs do
- require 'railties_path'
- FileUtils.cp(RAILTIES_PATH + '/environments/boot.rb', RAILS_ROOT + '/config/boot.rb')
+ invoke_from_app_generator :create_boot_file
+ end
+
+ desc "Update Prototype javascripts from your current rails install"
+ task :javascripts do
+ invoke_from_app_generator :create_prototype_files
+ end
+
+ desc "Generate dispatcher files in RAILS_ROOT/public"
+ task :generate_dispatchers do
+ invoke_from_app_generator :create_dispatch_files
end
-
+
+ desc "Add new scripts to the application script/ directory"
+ task :scripts do
+ invoke_from_app_generator :create_script_files
+ end
+
desc "Rename application.rb to application_controller.rb"
task :application_controller do
old_style = RAILS_ROOT + '/app/controllers/application.rb'
@@ -132,14 +129,5 @@ namespace :rails do
puts "#{old_style} has been renamed to #{new_style}, update your SCM as necessary"
end
end
-
- desc "Generate dispatcher files in RAILS_ROOT/public"
- task :generate_dispatchers do
- require 'railties_path'
- FileUtils.cp(RAILTIES_PATH + '/dispatches/config.ru', RAILS_ROOT + '/config.ru')
- FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.fcgi', RAILS_ROOT + '/public/dispatch.fcgi')
- FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.rb')
- FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.cgi')
- end
end
end
diff --git a/railties/lib/vendor/thor-0.11.3/CHANGELOG.rdoc b/railties/lib/vendor/thor-0.11.3/CHANGELOG.rdoc
new file mode 100644
index 0000000000..544dde8c02
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/CHANGELOG.rdoc
@@ -0,0 +1,75 @@
+== TODO
+
+* Improve spec coverage for Thor::Runner
+* Improve help output to list shorthand switches, too
+
+== Current
+
+* BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore
+ since it wrong behavior to the invocation system.
+
+* thor help now show information about any class/task. All those calls are
+ possible:
+
+ thor help describe
+ thor help describe:amazing
+
+ Or even with default namespaces:
+
+ thor help :spec
+
+* Thor::Runner now invokes the default task if none is supplied:
+
+ thor describe # invokes the default task, usually help
+
+* Thor::Runner now works with mappings:
+
+ thor describe -h
+
+* Added some documentation and code refactoring.
+
+== 0.9.8, released 2008-10-20
+
+* Fixed some tiny issues that were introduced lately.
+
+== 0.9.7, released 2008-10-13
+
+* Setting global method options on the initialize method works as expected:
+ All other tasks will accept these global options in addition to their own.
+* Added 'group' notion to Thor task sets (class Thor); by default all tasks
+ are in the 'standard' group. Running 'thor -T' will only show the standard
+ tasks - adding --all will show all tasks. You can also filter on a specific
+ group using the --group option: thor -T --group advanced
+
+== 0.9.6, released 2008-09-13
+
+* Generic improvements
+
+== 0.9.5, released 2008-08-27
+
+* Improve Windows compatibility
+* Update (incorrect) README and task.thor sample file
+* Options hash is now frozen (once returned)
+* Allow magic predicates on options object. For instance: `options.force?`
+* Add support for :numeric type
+* BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f])
+* Allow specifying optional args with default values: method_options(:user => "mislav")
+* Don't write options for nil or false values. This allows, for example, turning color off when running specs.
+* Exit with the status of the spec command to help CI stuff out some.
+
+== 0.9.4, released 2008-08-13
+
+* Try to add Windows compatibility.
+* BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore
+* Allow options at the beginning of the argument list as well as the end.
+* Make options available with symbol keys in addition to string keys.
+* Allow true to be passed to Thor#method_options to denote a boolean option.
+* If loading a thor file fails, don't give up, just print a warning and keep going.
+* Make sure that we re-raise errors if they happened further down the pipe than we care about.
+* Only delete the old file on updating when the installation of the new one is a success
+* Make it Ruby 1.8.5 compatible.
+* Don't raise an error if a boolean switch is defined multiple times.
+* Thor::Options now doesn't parse through things that look like options but aren't.
+* Add URI detection to install task, and make sure we don't append ".thor" to URIs
+* Add rake2thor to the gem binfiles.
+* Make sure local Thorfiles override system-wide ones.
diff --git a/railties/lib/vendor/thor-0.11.3/LICENSE b/railties/lib/vendor/thor-0.11.3/LICENSE
new file mode 100644
index 0000000000..98722da459
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Yehuda Katz
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/railties/lib/vendor/thor-0.11.3/README.markdown b/railties/lib/vendor/thor-0.11.3/README.markdown
new file mode 100644
index 0000000000..a1d7259775
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/README.markdown
@@ -0,0 +1,247 @@
+thor
+====
+
+Map options to a class. Simply create a class with the appropriate annotations
+and have options automatically map to functions and parameters.
+
+Example:
+
+ class App < Thor # [1]
+ map "-L" => :list # [2]
+
+ desc "install APP_NAME", "install one of the available apps" # [3]
+ method_options :force => :boolean, :alias => :string # [4]
+ def install(name)
+ user_alias = options[:alias]
+ if options.force?
+ # do something
+ end
+ # other code
+ end
+
+ desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
+ def list(search="")
+ # list everything
+ end
+ end
+
+Thor automatically maps commands as such:
+
+ thor app:install myname --force
+
+That gets converted to:
+
+ App.new.install("myname")
+ # with {'force' => true} as options hash
+
+1. Inherit from Thor to turn a class into an option mapper
+2. Map additional non-valid identifiers to specific methods. In this case, convert -L to :list
+3. Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description
+4. Provide any additional options that will be available the instance method options.
+
+Types for `method_options`
+--------------------------
+
+<dl>
+ <dt><code>:boolean</code></dt>
+ <dd>is parsed as --option or --option=true</dd>
+ <dt><code>:string</code></dt>
+ <dd>is parsed as --option=VALUE</dd>
+ <dt><code>:numeric</code></dt>
+ <dd>is parsed as --option=N</dd>
+ <dt><code>:array</code></dt>
+ <dd>is parsed as --option=one two three</dd>
+ <dt><code>:hash</code></dt>
+ <dd>is parsed as --option=key:value key:value key:value</dd>
+</dl>
+
+Besides, method_option allows a default value to be given, examples:
+
+ method_options :force => false
+ #=> Creates a boolean option with default value false
+
+ method_options :alias => "bar"
+ #=> Creates a string option with default value "bar"
+
+ method_options :threshold => 3.0
+ #=> Creates a numeric option with default value 3.0
+
+You can also supply :option => :required to mark an option as required. The
+type is assumed to be string. If you want a required hash with default values
+as option, you can use `method_option` which uses a more declarative style:
+
+ method_option :attributes, :type => :hash, :default => {}, :required => true
+
+All arguments can be set to nil (except required arguments), by suppling a no or
+skip variant. For example:
+
+ thor app name --no-attributes
+
+In previous versions, aliases for options were created automatically, but now
+they should be explicit. You can supply aliases in both short and declarative
+styles:
+
+ method_options %w( force -f ) => :boolean
+
+Or:
+
+ method_option :force, :type => :boolean, :aliases => "-f"
+
+You can supply as many aliases as you want.
+
+NOTE: Type :optional available in Thor 0.9.0 was deprecated. Use :string or :boolean instead.
+
+Namespaces
+----------
+
+By default, your Thor tasks are invoked using Ruby namespace. In the example
+above, tasks are invoked as:
+
+ thor app:install name --force
+
+However, you could namespace your class as:
+
+ module Sinatra
+ class App < Thor
+ # tasks
+ end
+ end
+
+And then you should invoke your tasks as:
+
+ thor sinatra:app:install name --force
+
+If desired, you can change the namespace:
+
+ module Sinatra
+ class App < Thor
+ namespace :myapp
+ # tasks
+ end
+ end
+
+And then your tasks hould be invoked as:
+
+ thor myapp:install name --force
+
+Invocations
+-----------
+
+Thor comes with a invocation-dependency system as well which allows a task to be
+invoked only once. For example:
+
+ class Counter < Thor
+ desc "one", "Prints 1, 2, 3"
+ def one
+ puts 1
+ invoke :two
+ invoke :three
+ end
+
+ desc "two", "Prints 2, 3"
+ def two
+ puts 2
+ invoke :three
+ end
+
+ desc "three", "Prints 3"
+ def three
+ puts 3
+ end
+ end
+
+When invoking the task one:
+
+ thor counter:one
+
+The output is "1 2 3", which means that the three task was invoked only once.
+You can even invoke tasks from another class, so be sure to check the
+documentation.
+
+Thor::Group
+-----------
+
+Thor has a special class called Thor::Group. The main difference to Thor class
+is that it invokes all tasks at once. The example above could be rewritten in
+Thor::Group as this:
+
+ class Counter < Thor::Group
+ desc "Prints 1, 2, 3"
+
+ def one
+ puts 1
+ end
+
+ def two
+ puts 2
+ end
+
+ def three
+ puts 3
+ end
+ end
+
+When invoked:
+
+ thor counter
+
+It prints "1 2 3" as well. Notice you should described (desc) only the class
+and not each task anymore. Thor::Group is a great tool to create generators,
+since you can define several steps which are invoked in the order they are
+defined (Thor::Group is the tool use in generators in Rails 3.0).
+
+Besides, Thor::Group can parse arguments and options as Thor tasks:
+
+ class Counter < Thor::Group
+ # number will be available as attr_accessor
+ argument :number, :type => :numeric, :desc => "The number to start counting"
+ desc "Prints the 'number' given upto 'number+2'"
+
+ def one
+ puts number + 0
+ end
+
+ def two
+ puts number + 1
+ end
+
+ def three
+ puts number + 2
+ end
+ end
+
+The counter above expects one parameter and has the folling outputs:
+
+ thor counter 5
+ # Prints "5 6 7"
+
+ thor counter 11
+ # Prints "11 12 13"
+
+You can also give options to Thor::Group, but instead of using `method_option` and
+`method_options`, you should use `class_option` and `class_options`. Both argument
+and class_options methods are available to Thor class as well.
+
+Actions
+-------
+
+Thor comes with several actions which helps with script and generator tasks. You
+might be familiar with them since some came from Rails Templates. They are: `say`,
+`ask`, `yes?`, `no?`, `add_file`, `remove_file`, `copy_file`, `template`,
+`directory`, `inside`, `run`, `inject_into_file` and a couple more.
+
+To use them, you just need to include Thor::Actions in your Thor classes:
+
+ class App < Thor
+ include Thor::Actions
+ # tasks
+ end
+
+Some actions like copy file requires that a class method called source_root is
+defined in your class. This is the directory where your templates should be
+placed. Be sure to check the documentation.
+
+License
+-------
+
+See MIT LICENSE.
diff --git a/railties/lib/vendor/thor-0.11.3/bin/rake2thor b/railties/lib/vendor/thor-0.11.3/bin/rake2thor
new file mode 100755
index 0000000000..50c7410d80
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/bin/rake2thor
@@ -0,0 +1,87 @@
+#!/usr/bin/env ruby
+
+require 'rubygems'
+require 'ruby2ruby'
+require 'parse_tree'
+if Ruby2Ruby::VERSION >= "1.2.0"
+ require 'parse_tree_extensions'
+end
+require 'rake'
+
+input = ARGV[0] || 'Rakefile'
+output = ARGV[1] || 'Thorfile'
+
+$requires = []
+
+module Kernel
+ def require_with_record(file)
+ $requires << file if caller[1] =~ /rake2thor:/
+ require_without_record file
+ end
+ alias_method :require_without_record, :require
+ alias_method :require, :require_with_record
+end
+
+load input
+
+@private_methods = []
+
+def file_task_name(name)
+ "compile_" + name.gsub('/', '_slash_').gsub('.', '_dot_').gsub(/\W/, '_')
+end
+
+def method_for_task(task)
+ file_task = task.is_a?(Rake::FileTask)
+ comment = task.instance_variable_get('@comment')
+ prereqs = task.instance_variable_get('@prerequisites').select(&Rake::Task.method(:task_defined?))
+ actions = task.instance_variable_get('@actions')
+ name = task.name.gsub(/^([^:]+:)+/, '')
+ name = file_task_name(name) if file_task
+ meth = ''
+
+ meth << "desc #{name.inspect}, #{comment.inspect}\n" if comment
+ meth << "def #{name}\n"
+
+ meth << prereqs.map do |pre|
+ pre = pre.to_s
+ pre = file_task_name(pre) if Rake::Task[pre].is_a?(Rake::FileTask)
+ ' ' + pre
+ end.join("\n")
+
+ meth << "\n\n" unless prereqs.empty? || actions.empty?
+
+ meth << actions.map do |act|
+ act = act.to_ruby
+ unless act.gsub!(/^proc \{ \|(\w+)\|\n/,
+ " \\1 = Struct.new(:name).new(#{name.inspect}) # A crude mock Rake::Task object\n")
+ act.gsub!(/^proc \{\n/, '')
+ end
+ act.gsub(/\n\}$/, '')
+ end.join("\n")
+
+ meth << "\nend"
+
+ if file_task
+ @private_methods << meth
+ return
+ end
+
+ meth
+end
+
+body = Rake::Task.tasks.map(&method(:method_for_task)).compact.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
+
+unless @private_methods.empty?
+ body << "\n\n private\n\n"
+ body << @private_methods.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
+end
+
+requires = $requires.map { |r| "require #{r.inspect}" }.join("\n")
+
+File.open(output, 'w') { |f| f.write(<<END.lstrip) }
+#{requires}
+
+class Default < Thor
+#{body}
+end
+END
diff --git a/railties/lib/vendor/thor-0.11.3/bin/thor b/railties/lib/vendor/thor-0.11.3/bin/thor
new file mode 100755
index 0000000000..eaf849fb4a
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/bin/thor
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+# -*- mode: ruby -*-
+
+require File.join(File.dirname(__FILE__), '..', 'lib', 'thor')
+require 'thor/runner'
+
+Thor::Runner.start
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor.rb b/railties/lib/vendor/thor-0.11.3/lib/thor.rb
new file mode 100644
index 0000000000..f65455cdda
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor.rb
@@ -0,0 +1,240 @@
+$:.unshift File.expand_path(File.dirname(__FILE__))
+require 'thor/base'
+require 'thor/group'
+require 'thor/actions'
+
+class Thor
+ class << self
+ # Sets the default task when thor is executed without an explicit task to be called.
+ #
+ # ==== Parameters
+ # meth<Symbol>:: name of the defaut task
+ #
+ def default_task(meth=nil)
+ case meth
+ when :none
+ @default_task = 'help'
+ when nil
+ @default_task ||= from_superclass(:default_task, 'help')
+ else
+ @default_task = meth.to_s
+ end
+ end
+
+ # Defines the usage and the description of the next task.
+ #
+ # ==== Parameters
+ # usage<String>
+ # description<String>
+ #
+ def desc(usage, description, options={})
+ if options[:for]
+ task = find_and_refresh_task(options[:for])
+ task.usage = usage if usage
+ task.description = description if description
+ else
+ @usage, @desc = usage, description
+ end
+ end
+
+ # Maps an input to a task. If you define:
+ #
+ # map "-T" => "list"
+ #
+ # Running:
+ #
+ # thor -T
+ #
+ # Will invoke the list task.
+ #
+ # ==== Parameters
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task.
+ #
+ def map(mappings=nil)
+ @map ||= from_superclass(:map, {})
+
+ if mappings
+ mappings.each do |key, value|
+ if key.respond_to?(:each)
+ key.each {|subkey| @map[subkey] = value}
+ else
+ @map[key] = value
+ end
+ end
+ end
+
+ @map
+ end
+
+ # Declares the options for the next task to be declared.
+ #
+ # ==== Parameters
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
+ # or :required (string). If you give a value, the type of the value is used.
+ #
+ def method_options(options=nil)
+ @method_options ||= {}
+ build_options(options, @method_options) if options
+ @method_options
+ end
+
+ # Adds an option to the set of class options. If :for is given as option,
+ # it allows you to change the options from a previous defined task.
+ #
+ # def previous_task
+ # # magic
+ # end
+ #
+ # method_options :foo => :bar, :for => :previous_task
+ #
+ # def next_task
+ # # magic
+ # end
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described below.
+ #
+ # ==== Options
+ # :desc - Description for the argument.
+ # :required - If the argument is required or not.
+ # :default - Default value for this argument. It cannot be required and have default values.
+ # :aliases - Aliases for this option.
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric, :boolean or :default.
+ # Default accepts arguments as booleans (--switch) or as strings (--switch=VALUE).
+ # :group - The group for this options. Use by class options to output options in different levels.
+ # :banner - String to show on usage notes.
+ #
+ def method_option(name, options={})
+ scope = if options[:for]
+ find_and_refresh_task(options[:for]).options
+ else
+ method_options
+ end
+
+ build_option(name, options, scope)
+ end
+
+ # Parses the task and options from the given args, instantiate the class
+ # and invoke the task. This method is used when the arguments must be parsed
+ # from an array. If you are inside Ruby and want to use a Thor class, you
+ # can simply initialize it:
+ #
+ # script = MyScript.new(args, options, config)
+ # script.invoke(:task, first_arg, second_arg, third_arg)
+ #
+ def start(given_args=ARGV, config={})
+ super do
+ meth = normalize_task_name(given_args.shift)
+ task = all_tasks[meth]
+
+ if task
+ args, opts = Thor::Options.split(given_args)
+ config.merge!(:task_options => task.options)
+ else
+ args, opts = given_args, {}
+ end
+
+ task ||= Task.dynamic(meth)
+ trailing = args[Range.new(arguments.size, -1)]
+ new(args, opts, config).invoke(task, trailing || [])
+ end
+ end
+
+ # Prints help information. If a task name is given, it shows information
+ # only about the specific task.
+ #
+ # ==== Parameters
+ # meth<String>:: An optional task name to print usage information about.
+ #
+ # ==== Options
+ # namespace:: When true, shows the namespace in the output before the usage.
+ # skip_inherited:: When true, does not show tasks from superclass.
+ #
+ def help(shell, meth=nil, options={})
+ meth, options = nil, meth if meth.is_a?(Hash)
+
+ if meth
+ task = all_tasks[meth]
+ raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task
+
+ shell.say "Usage:"
+ shell.say " #{banner(task, options[:namespace])}"
+ shell.say
+ class_options_help(shell, "Class")
+ shell.say task.description
+ else
+ list = (options[:short] ? tasks : all_tasks).map do |_, task|
+ item = [ " " + banner(task, options[:namespace]) ]
+ item << if task.short_description
+ "\n # #{task.short_description}\n"
+ else
+ "\n"
+ end
+ end
+
+ if options[:short]
+ shell.print_table(list)
+ else
+ shell.say "Tasks:"
+ shell.print_table(list)
+ class_options_help(shell, "Class")
+ end
+ end
+ end
+
+ protected
+
+ # The banner for this class. You can customize it if you are invoking the
+ # thor class by another means which is not the Thor::Runner. It receives
+ # the task that is going to be invoked and if the namespace should be
+ # displayed.
+ #
+ def banner(task, namespace=true) #:nodoc:
+ task.formatted_usage(self, namespace)
+ end
+
+ def baseclass #:nodoc:
+ Thor
+ end
+
+ def create_task(meth) #:nodoc:
+ if @usage && @desc
+ tasks[meth.to_s] = Thor::Task.new(meth, @desc, @usage, method_options)
+ @usage, @desc, @method_options = nil
+ true
+ elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing
+ true
+ else
+ puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " <<
+ "Call desc if you want this method to be available as task or declare it inside a " <<
+ "no_tasks{} block. Invoked from #{caller[1].inspect}."
+ false
+ end
+ end
+
+ def initialize_added #:nodoc:
+ class_options.merge!(method_options)
+ @method_options = nil
+ end
+
+ # Receives a task name (can be nil), and try to get a map from it.
+ # If a map can't be found use the sent name or the default task.
+ #
+ def normalize_task_name(meth) #:nodoc:
+ mapping = map[meth.to_s]
+ meth = mapping || meth || default_task
+ meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
+ end
+ end
+
+ include Thor::Base
+
+ map HELP_MAPPINGS => :help
+
+ desc "help [TASK]", "Describe available tasks or one specific task"
+ def help(task=nil)
+ self.class.help(shell, task, :namespace => task && task.include?(?:))
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions.rb
new file mode 100644
index 0000000000..b8cfde1940
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions.rb
@@ -0,0 +1,270 @@
+require 'fileutils'
+
+Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action|
+ require action
+end
+
+class Thor
+ module Actions
+ attr_accessor :behavior
+
+ # On inclusion, add some options to base.
+ #
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+ return unless base.respond_to?(:class_option)
+
+ base.class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
+ :desc => "Run but do not make any changes"
+
+ base.class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
+ :desc => "Overwrite files that already exist"
+
+ base.class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
+ :desc => "Skip files that already exist"
+
+ base.class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
+ :desc => "Supress status output"
+ end
+
+ module ClassMethods
+ # Hold source paths for one Thor instance. source_paths_for_search is the
+ # method responsible to gather source_paths from this current class,
+ # inherited paths and the source root.
+ #
+ def source_paths
+ @source_paths ||= []
+ end
+
+ # Returns the source paths in the following order:
+ #
+ # 1) This class source paths
+ # 2) Source root
+ # 3) Parents source paths
+ #
+ def source_paths_for_search
+ @source_paths_for_search ||= begin
+ paths = []
+ paths += self.source_paths
+ paths << self.source_root if self.respond_to?(:source_root)
+ paths += from_superclass(:source_paths, [])
+ paths
+ end
+ end
+ end
+
+ # Extends initializer to add more configuration options.
+ #
+ # ==== Configuration
+ # behavior<Symbol>:: The actions default behavior. Can be :invoke or :revoke.
+ # It also accepts :force, :skip and :pretend to set the behavior
+ # and the respective option.
+ #
+ # destination_root<String>:: The root directory needed for some actions. It's also known
+ # as destination root.
+ #
+ def initialize(args=[], options={}, config={})
+ self.behavior = case config[:behavior].to_s
+ when "force", "skip"
+ _cleanup_options_and_set(options, config[:behavior])
+ :invoke
+ when "revoke"
+ :revoke
+ else
+ :invoke
+ end
+
+ super
+ self.destination_root = config[:destination_root]
+ end
+
+ # Wraps an action object and call it accordingly to the thor class behavior.
+ #
+ def action(instance)
+ if behavior == :revoke
+ instance.revoke!
+ else
+ instance.invoke!
+ end
+ end
+
+ # Returns the root for this thor class (also aliased as destination root).
+ #
+ def destination_root
+ @destination_stack.last
+ end
+
+ # Sets the root for this thor class. Relatives path are added to the
+ # directory where the script was invoked and expanded.
+ #
+ def destination_root=(root)
+ @destination_stack ||= []
+ @destination_stack[0] = File.expand_path(root || '')
+ end
+
+ # Returns the given path relative to the absolute root (ie, root where
+ # the script started).
+ #
+ def relative_to_original_destination_root(path, remove_dot=true)
+ path = path.gsub(@destination_stack[0], '.')
+ remove_dot ? (path[2..-1] || '') : path
+ end
+
+ # Receives a file or directory and search for it in the source paths.
+ #
+ def find_in_source_paths(file)
+ relative_root = relative_to_original_destination_root(destination_root, false)
+ paths = self.class.source_paths_for_search
+
+ paths.each do |source|
+ source_file = File.expand_path(file, File.join(source, relative_root))
+ return source_file if File.exists?(source_file)
+ end
+
+ if paths.empty?
+ raise Error, "You don't have any source path defined for class #{self.class.name}. To fix this, " <<
+ "you can define a source_root in your class."
+ else
+ raise Error, "Could not find #{file.inspect} in source paths."
+ end
+ end
+
+ # Do something in the root or on a provided subfolder. If a relative path
+ # is given it's referenced from the current root. The full path is yielded
+ # to the block you provide. The path is set back to the previous path when
+ # the method exits.
+ #
+ # ==== Parameters
+ # dir<String>:: the directory to move to.
+ # config<Hash>:: give :verbose => true to log and use padding.
+ #
+ def inside(dir='', config={}, &block)
+ verbose = config.fetch(:verbose, false)
+
+ say_status :inside, dir, verbose
+ shell.padding += 1 if verbose
+ @destination_stack.push File.expand_path(dir, destination_root)
+
+ FileUtils.mkdir_p(destination_root) unless File.exist?(destination_root)
+ FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
+
+ @destination_stack.pop
+ shell.padding -= 1 if verbose
+ end
+
+ # Goes to the root and execute the given block.
+ #
+ def in_root
+ inside(@destination_stack.first) { yield }
+ end
+
+ # Loads an external file and execute it in the instance binding.
+ #
+ # ==== Parameters
+ # path<String>:: The path to the file to execute. Can be a web address or
+ # a relative path from the source root.
+ #
+ # ==== Examples
+ #
+ # apply "http://gist.github.com/103208"
+ #
+ # apply "recipes/jquery.rb"
+ #
+ def apply(path, config={})
+ verbose = config.fetch(:verbose, true)
+ path = find_in_source_paths(path) unless path =~ /^http\:\/\//
+
+ say_status :apply, path, verbose
+ shell.padding += 1 if verbose
+ instance_eval(open(path).read)
+ shell.padding -= 1 if verbose
+ end
+
+ # Executes a command.
+ #
+ # ==== Parameters
+ # command<String>:: the command to be executed.
+ # config<Hash>:: give :verbose => false to not log the status. Specify :with
+ # to append an executable to command executation.
+ #
+ # ==== Example
+ #
+ # inside('vendor') do
+ # run('ln -s ~/edge rails')
+ # end
+ #
+ def run(command, config={})
+ return unless behavior == :invoke
+
+ destination = relative_to_original_destination_root(destination_root, false)
+ desc = "#{command} from #{destination.inspect}"
+
+ if config[:with]
+ desc = "#{File.basename(config[:with].to_s)} #{desc}"
+ command = "#{config[:with]} #{command}"
+ end
+
+ say_status :run, desc, config.fetch(:verbose, true)
+ `#{command}` unless options[:pretend]
+ end
+
+ # Executes a ruby script (taking into account WIN32 platform quirks).
+ #
+ # ==== Parameters
+ # command<String>:: the command to be executed.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ def run_ruby_script(command, config={})
+ return unless behavior == :invoke
+ run "#{command}", config.merge(:with => Thor::Util.ruby_command)
+ end
+
+ # Run a thor command. A hash of options can be given and it's converted to
+ # switches.
+ #
+ # ==== Parameters
+ # task<String>:: the task to be invoked
+ # args<Array>:: arguments to the task
+ # config<Hash>:: give :verbose => false to not log the status. Other options
+ # are given as parameter to Thor.
+ #
+ # ==== Examples
+ #
+ # thor :install, "http://gist.github.com/103208"
+ # #=> thor install http://gist.github.com/103208
+ #
+ # thor :list, :all => true, :substring => 'rails'
+ # #=> thor list --all --substring=rails
+ #
+ def thor(task, *args)
+ config = args.last.is_a?(Hash) ? args.pop : {}
+ verbose = config.key?(:verbose) ? config.delete(:verbose) : true
+
+ args.unshift task
+ args.push Thor::Options.to_switches(config)
+ command = args.join(' ').strip
+
+ run command, :with => :thor, :verbose => verbose
+ end
+
+ protected
+
+ # Allow current root to be shared between invocations.
+ #
+ def _shared_configuration #:nodoc:
+ super.merge!(:destination_root => self.destination_root)
+ end
+
+ def _cleanup_options_and_set(options, key) #:nodoc:
+ case options
+ when Array
+ %w(--force -f --skip -s).each { |i| options.delete(i) }
+ options << "--#{key}"
+ when Hash
+ [:force, :skip, "force", "skip"].each { |i| options.delete(i) }
+ options.merge!(key => true)
+ end
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/create_file.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/create_file.rb
new file mode 100644
index 0000000000..8f6badee27
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/create_file.rb
@@ -0,0 +1,102 @@
+require 'thor/actions/empty_directory'
+
+class Thor
+ module Actions
+
+ # Create a new file relative to the destination root with the given data,
+ # which is the return value of a block or a data string.
+ #
+ # ==== Parameters
+ # destination<String>:: the relative path to the destination root.
+ # data<String|NilClass>:: the data to append to the file.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # create_file "lib/fun_party.rb" do
+ # hostname = ask("What is the virtual hostname I should use?")
+ # "vhost.name = #{hostname}"
+ # end
+ #
+ # create_file "config/apach.conf", "your apache config"
+ #
+ def create_file(destination, data=nil, config={}, &block)
+ action CreateFile.new(self, destination, block || data.to_s, config)
+ end
+ alias :add_file :create_file
+
+ # AddFile is a subset of Template, which instead of rendering a file with
+ # ERB, it gets the content from the user.
+ #
+ class CreateFile < EmptyDirectory #:nodoc:
+ attr_reader :data
+
+ def initialize(base, destination, data, config={})
+ @data = data
+ super(base, destination, config)
+ end
+
+ # Checks if the content of the file at the destination is identical to the rendered result.
+ #
+ # ==== Returns
+ # Boolean:: true if it is identical, false otherwise.
+ #
+ def identical?
+ exists? && File.read(destination) == render
+ end
+
+ # Holds the content to be added to the file.
+ #
+ def render
+ @render ||= if data.is_a?(Proc)
+ data.call
+ else
+ data
+ end
+ end
+
+ def invoke!
+ invoke_with_conflict_check do
+ FileUtils.mkdir_p(File.dirname(destination))
+ File.open(destination, 'w'){ |f| f.write render }
+ end
+ end
+
+ protected
+
+ # Now on conflict we check if the file is identical or not.
+ #
+ def on_conflict_behavior(&block)
+ if identical?
+ say_status :identical, :blue
+ else
+ options = base.options.merge(config)
+ force_or_skip_or_conflict(options[:force], options[:skip], &block)
+ end
+ end
+
+ # If force is true, run the action, otherwise check if it's not being
+ # skipped. If both are false, show the file_collision menu, if the menu
+ # returns true, force it, otherwise skip.
+ #
+ def force_or_skip_or_conflict(force, skip, &block)
+ if force
+ say_status :force, :yellow
+ block.call unless pretend?
+ elsif skip
+ say_status :skip, :yellow
+ else
+ say_status :conflict, :red
+ force_or_skip_or_conflict(force_on_collision?, true, &block)
+ end
+ end
+
+ # Shows the file collision menu to the user and gets the result.
+ #
+ def force_on_collision?
+ base.shell.file_collision(destination){ render }
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/directory.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/directory.rb
new file mode 100644
index 0000000000..e33639f4e5
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/directory.rb
@@ -0,0 +1,87 @@
+require 'thor/actions/empty_directory'
+
+class Thor
+ module Actions
+
+ # Copies interactively the files from source directory to root directory.
+ # If any of the files finishes with .tt, it's considered to be a template
+ # and is placed in the destination without the extension .tt. If any
+ # empty directory is found, it's copied and all .empty_directory files are
+ # ignored. Remember that file paths can also be encoded, let's suppose a doc
+ # directory with the following files:
+ #
+ # doc/
+ # components/.empty_directory
+ # README
+ # rdoc.rb.tt
+ # %app_name%.rb
+ #
+ # When invoked as:
+ #
+ # directory "doc"
+ #
+ # It will create a doc directory in the destination with the following
+ # files (assuming that the app_name is "blog"):
+ #
+ # doc/
+ # components/
+ # README
+ # rdoc.rb
+ # blog.rb
+ #
+ # ==== Parameters
+ # source<String>:: the relative path to the source root.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ # If :recursive => false, does not look for paths recursively.
+ #
+ # ==== Examples
+ #
+ # directory "doc"
+ # directory "doc", "docs", :recursive => false
+ #
+ def directory(source, destination=nil, config={})
+ action Directory.new(self, source, destination || source, config)
+ end
+
+ class Directory < EmptyDirectory #:nodoc:
+ attr_reader :source
+
+ def initialize(base, source, destination=nil, config={})
+ @source = File.expand_path(base.find_in_source_paths(source.to_s))
+ super(base, destination, { :recursive => true }.merge(config))
+ end
+
+ def invoke!
+ base.empty_directory given_destination, config
+ execute!
+ end
+
+ def revoke!
+ execute!
+ end
+
+ protected
+
+ def execute!
+ lookup = config[:recursive] ? File.join(source, '**') : source
+ lookup = File.join(lookup, '{*,.[a-z]*}')
+
+ Dir[lookup].each do |file_source|
+ next if File.directory?(file_source)
+ file_destination = File.join(given_destination, file_source.gsub(source, '.'))
+
+ case file_source
+ when /\.empty_directory$/
+ base.empty_directory(File.dirname(file_destination), config)
+ when /\.tt$/
+ base.template(file_source, file_destination[0..-4], config)
+ else
+ base.copy_file(file_source, file_destination, config)
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/empty_directory.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/empty_directory.rb
new file mode 100644
index 0000000000..03c1fe4af1
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/empty_directory.rb
@@ -0,0 +1,133 @@
+class Thor
+ module Actions
+
+ # Creates an empty directory.
+ #
+ # ==== Parameters
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # empty_directory "doc"
+ #
+ def empty_directory(destination, config={})
+ action EmptyDirectory.new(self, destination, config)
+ end
+
+ # Class which holds create directory logic. This is the base class for
+ # other actions like create_file and directory.
+ #
+ # This implementation is based in Templater actions, created by Jonas Nicklas
+ # and Michael S. Klishin under MIT LICENSE.
+ #
+ class EmptyDirectory #:nodoc:
+ attr_reader :base, :destination, :given_destination, :relative_destination, :config
+
+ # Initializes given the source and destination.
+ #
+ # ==== Parameters
+ # base<Thor::Base>:: A Thor::Base instance
+ # source<String>:: Relative path to the source of this file
+ # destination<String>:: Relative path to the destination of this file
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ def initialize(base, destination, config={})
+ @base, @config = base, { :verbose => true }.merge(config)
+ self.destination = destination
+ end
+
+ # Checks if the destination file already exists.
+ #
+ # ==== Returns
+ # Boolean:: true if the file exists, false otherwise.
+ #
+ def exists?
+ ::File.exists?(destination)
+ end
+
+ def invoke!
+ invoke_with_conflict_check do
+ ::FileUtils.mkdir_p(destination)
+ end
+ end
+
+ def revoke!
+ say_status :remove, :red
+ ::FileUtils.rm_rf(destination) if !pretend? && exists?
+ end
+
+ protected
+
+ # Shortcut for pretend.
+ #
+ def pretend?
+ base.options[:pretend]
+ end
+
+ # Sets the absolute destination value from a relative destination value.
+ # It also stores the given and relative destination. Let's suppose our
+ # script is being executed on "dest", it sets the destination root to
+ # "dest". The destination, given_destination and relative_destination
+ # are related in the following way:
+ #
+ # inside "bar" do
+ # empty_directory "baz"
+ # end
+ #
+ # destination #=> dest/bar/baz
+ # relative_destination #=> bar/baz
+ # given_destination #=> baz
+ #
+ def destination=(destination)
+ if destination
+ @given_destination = convert_encoded_instructions(destination.to_s)
+ @destination = ::File.expand_path(@given_destination, base.destination_root)
+ @relative_destination = base.relative_to_original_destination_root(@destination)
+ end
+ end
+
+ # Filenames in the encoded form are converted. If you have a file:
+ #
+ # %class_name%.rb
+ #
+ # It gets the class name from the base and replace it:
+ #
+ # user.rb
+ #
+ def convert_encoded_instructions(filename)
+ filename.gsub(/%(.*?)%/) do |string|
+ instruction = $1.strip
+ base.respond_to?(instruction) ? base.send(instruction) : string
+ end
+ end
+
+ # Receives a hash of options and just execute the block if some
+ # conditions are met.
+ #
+ def invoke_with_conflict_check(&block)
+ if exists?
+ on_conflict_behavior(&block)
+ else
+ say_status :create, :green
+ block.call unless pretend?
+ end
+
+ destination
+ end
+
+ # What to do when the destination file already exists.
+ #
+ def on_conflict_behavior(&block)
+ say_status :exist, :blue
+ end
+
+ # Shortcut to say_status shell method.
+ #
+ def say_status(status, color)
+ base.shell.say_status status, relative_destination, color if config[:verbose]
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/file_manipulation.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/file_manipulation.rb
new file mode 100644
index 0000000000..74c157ba8c
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/file_manipulation.rb
@@ -0,0 +1,195 @@
+require 'erb'
+require 'open-uri'
+
+class Thor
+ module Actions
+
+ # Copies the file from the relative source to the relative destination. If
+ # the destination is not given it's assumed to be equal to the source.
+ #
+ # ==== Parameters
+ # source<String>:: the relative path to the source root.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # copy_file "README", "doc/README"
+ #
+ # copy_file "doc/README"
+ #
+ def copy_file(source, destination=nil, config={})
+ destination ||= source
+ source = File.expand_path(find_in_source_paths(source.to_s))
+
+ create_file destination, nil, config do
+ File.read(source)
+ end
+ end
+
+ # Gets the content at the given address and places it at the given relative
+ # destination. If a block is given instead of destination, the content of
+ # the url is yielded and used as location.
+ #
+ # ==== Parameters
+ # source<String>:: the address of the given content.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # get "http://gist.github.com/103208", "doc/README"
+ #
+ # get "http://gist.github.com/103208" do |content|
+ # content.split("\n").first
+ # end
+ #
+ def get(source, destination=nil, config={}, &block)
+ source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\//
+ render = open(source).read
+
+ destination ||= if block_given?
+ block.arity == 1 ? block.call(render) : block.call
+ else
+ File.basename(source)
+ end
+
+ create_file destination, render, config
+ end
+
+ # Gets an ERB template at the relative source, executes it and makes a copy
+ # at the relative destination. If the destination is not given it's assumed
+ # to be equal to the source removing .tt from the filename.
+ #
+ # ==== Parameters
+ # source<String>:: the relative path to the source root.
+ # destination<String>:: the relative path to the destination root.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Examples
+ #
+ # template "README", "doc/README"
+ #
+ # template "doc/README"
+ #
+ def template(source, destination=nil, config={})
+ destination ||= source
+ source = File.expand_path(find_in_source_paths(source.to_s))
+ context = instance_eval('binding')
+
+ create_file destination, nil, config do
+ ERB.new(::File.read(source), nil, '-').result(context)
+ end
+ end
+
+ # Changes the mode of the given file or directory.
+ #
+ # ==== Parameters
+ # mode<Integer>:: the file mode
+ # path<String>:: the name of the file to change mode
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # chmod "script/*", 0755
+ #
+ def chmod(path, mode, config={})
+ return unless behavior == :invoke
+ path = File.expand_path(path, destination_root)
+ say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+ FileUtils.chmod_R(mode, path) unless options[:pretend]
+ end
+
+ # Prepend text to a file.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # data<String>:: the data to prepend to the file, can be also given as a block.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # prepend_file 'config/environments/test.rb', 'config.gem "rspec"'
+ #
+ def prepend_file(path, data=nil, config={}, &block)
+ return unless behavior == :invoke
+ path = File.expand_path(path, destination_root)
+ say_status :prepend, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+
+ unless options[:pretend]
+ content = data || block.call
+ content << File.read(path)
+ File.open(path, 'wb') { |file| file.write(content) }
+ end
+ end
+
+ # Append text to a file.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # data<String>:: the data to append to the file, can be also given as a block.
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # append_file 'config/environments/test.rb', 'config.gem "rspec"'
+ #
+ def append_file(path, data=nil, config={}, &block)
+ return unless behavior == :invoke
+ path = File.expand_path(path, destination_root)
+ say_status :append, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+ File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend]
+ end
+
+ # Run a regular expression replacement on a file.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # flag<Regexp|String>:: the regexp or string to be replaced
+ # replacement<String>:: the replacement, can be also given as a block
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
+ #
+ # gsub_file 'README', /rake/, :green do |match|
+ # match << " no more. Use thor!"
+ # end
+ #
+ def gsub_file(path, flag, *args, &block)
+ return unless behavior == :invoke
+ config = args.last.is_a?(Hash) ? args.pop : {}
+
+ path = File.expand_path(path, destination_root)
+ say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+
+ unless options[:pretend]
+ content = File.read(path)
+ content.gsub!(flag, *args, &block)
+ File.open(path, 'wb') { |file| file.write(content) }
+ end
+ end
+
+ # Removes a file at the given location.
+ #
+ # ==== Parameters
+ # path<String>:: path of the file to be changed
+ # config<Hash>:: give :verbose => false to not log the status.
+ #
+ # ==== Example
+ #
+ # remove_file 'README'
+ # remove_file 'app/controllers/application_controller.rb'
+ #
+ def remove_file(path, config={})
+ return unless behavior == :invoke
+ path = File.expand_path(path, destination_root)
+
+ say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
+ ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path)
+ end
+ alias :remove_dir :remove_file
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/inject_into_file.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/inject_into_file.rb
new file mode 100644
index 0000000000..089bd894e4
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/inject_into_file.rb
@@ -0,0 +1,78 @@
+require 'thor/actions/empty_directory'
+
+class Thor
+ module Actions
+
+ # Injects the given content into a file. Different from append_file,
+ # prepend_file and gsub_file, this method is reversible. By this reason,
+ # the flag can only be strings. gsub_file is your friend if you need to
+ # deal with more complex cases.
+ #
+ # ==== Parameters
+ # destination<String>:: Relative path to the destination root
+ # data<String>:: Data to add to the file. Can be given as a block.
+ # config<Hash>:: give :verbose => false to not log the status and the flag
+ # for injection (:after or :before).
+ #
+ # ==== Examples
+ #
+ # inject_into_file "config/environment.rb", "config.gem thor", :after => "Rails::Initializer.run do |config|\n"
+ #
+ # inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
+ # gems = ask "Which gems would you like to add?"
+ # gems.split(" ").map{ |gem| " config.gem #{gem}" }.join("\n")
+ # end
+ #
+ def inject_into_file(destination, *args, &block)
+ if block_given?
+ data, config = block, args.shift
+ else
+ data, config = args.shift, args.shift
+ end
+
+ log_status = args.empty? || args.pop
+ action InjectIntoFile.new(self, destination, data, config)
+ end
+
+ class InjectIntoFile < EmptyDirectory
+ attr_reader :flag, :replacement
+
+ def initialize(base, destination, data, config)
+ super(base, destination, { :verbose => true }.merge(config))
+
+ data = data.call if data.is_a?(Proc)
+
+ @replacement = if @config.key?(:after)
+ @flag = @config.delete(:after)
+ @flag + data
+ else
+ @flag = @config.delete(:before)
+ data + @flag
+ end
+ end
+
+ def invoke!
+ say_status :inject, config[:verbose]
+ replace!(flag, replacement)
+ end
+
+ def revoke!
+ say_status :deinject, config[:verbose]
+ replace!(replacement, flag)
+ end
+
+ protected
+
+ # Adds the content to the file.
+ #
+ def replace!(regexp, string)
+ unless base.options[:pretend]
+ content = File.read(destination)
+ content.gsub!(regexp, string)
+ File.open(destination, 'wb') { |file| file.write(content) }
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/base.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/base.rb
new file mode 100644
index 0000000000..0bdcc1f4d5
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/base.rb
@@ -0,0 +1,516 @@
+require 'thor/core_ext/hash_with_indifferent_access'
+require 'thor/core_ext/ordered_hash'
+require 'thor/error'
+require 'thor/shell'
+require 'thor/invocation'
+require 'thor/parser'
+require 'thor/task'
+require 'thor/util'
+
+class Thor
+ HELP_MAPPINGS = %w(-h -? --help -D)
+ THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
+ action add_file create_file in_root inside run run_ruby_script)
+
+ module Base
+ attr_accessor :options
+
+ # It receives arguments in an Array and two hashes, one for options and
+ # other for configuration.
+ #
+ # Notice that it does not check if all required arguments were supplied.
+ # It should be done by the parser.
+ #
+ # ==== Parameters
+ # args<Array[Object]>:: An array of objects. The objects are applied to their
+ # respective accessors declared with <tt>argument</tt>.
+ #
+ # options<Hash>:: An options hash that will be available as self.options.
+ # The hash given is converted to a hash with indifferent
+ # access, magic predicates (options.skip?) and then frozen.
+ #
+ # config<Hash>:: Configuration for this Thor class.
+ #
+ def initialize(args=[], options={}, config={})
+ Thor::Arguments.parse(self.class.arguments, args).each do |key, value|
+ send("#{key}=", value)
+ end
+
+ parse_options = self.class.class_options
+
+ if options.is_a?(Array)
+ task_options = config.delete(:task_options) # hook for start
+ parse_options = parse_options.merge(task_options) if task_options
+ array_options, hash_options = options, {}
+ else
+ array_options, hash_options = [], options
+ end
+
+ options = Thor::Options.parse(parse_options, array_options)
+ self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).merge!(hash_options)
+ self.options.freeze
+ end
+
+ class << self
+ def included(base) #:nodoc:
+ base.send :extend, ClassMethods
+ base.send :include, Invocation
+ base.send :include, Shell
+ end
+
+ # Returns the classes that inherits from Thor or Thor::Group.
+ #
+ # ==== Returns
+ # Array[Class]
+ #
+ def subclasses
+ @subclasses ||= []
+ end
+
+ # Returns the files where the subclasses are kept.
+ #
+ # ==== Returns
+ # Hash[path<String> => Class]
+ #
+ def subclass_files
+ @subclass_files ||= Hash.new{ |h,k| h[k] = [] }
+ end
+
+ # Whenever a class inherits from Thor or Thor::Group, we should track the
+ # class and the file on Thor::Base. This is the method responsable for it.
+ # Also adds the source root to the source paths if the klass respond to it.
+ #
+ def register_klass_file(klass) #:nodoc:
+ file = caller[1].match(/(.*):\d+/)[1]
+ Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass)
+
+ file_subclasses = Thor::Base.subclass_files[File.expand_path(file)]
+ file_subclasses << klass unless file_subclasses.include?(klass)
+ end
+ end
+
+ module ClassMethods
+ # Adds an argument to the class and creates an attr_accessor for it.
+ #
+ # Arguments are different from options in several aspects. The first one
+ # is how they are parsed from the command line, arguments are retrieved
+ # from position:
+ #
+ # thor task NAME
+ #
+ # Instead of:
+ #
+ # thor task --name=NAME
+ #
+ # Besides, arguments are used inside your code as an accessor (self.argument),
+ # while options are all kept in a hash (self.options).
+ #
+ # Finally, arguments cannot have type :default or :boolean but can be
+ # optional (supplying :optional => :true or :required => false), although
+ # you cannot have a required argument after a non-required argument. If you
+ # try it, an error is raised.
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described below.
+ #
+ # ==== Options
+ # :desc - Description for the argument.
+ # :required - If the argument is required or not.
+ # :optional - If the argument is optional or not.
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric.
+ # :default - Default value for this argument. It cannot be required and have default values.
+ # :banner - String to show on usage notes.
+ #
+ # ==== Errors
+ # ArgumentError:: Raised if you supply a required argument after a non required one.
+ #
+ def argument(name, options={})
+ is_thor_reserved_word?(name, :argument)
+ no_tasks { attr_accessor name }
+
+ required = if options.key?(:optional)
+ !options[:optional]
+ elsif options.key?(:required)
+ options[:required]
+ else
+ options[:default].nil?
+ end
+
+ remove_argument name
+
+ arguments.each do |argument|
+ next if argument.required?
+ raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
+ "the non-required argument #{argument.human_name.inspect}."
+ end if required
+
+ arguments << Thor::Argument.new(name, options[:desc], required, options[:type],
+ options[:default], options[:banner])
+ end
+
+ # Returns this class arguments, looking up in the ancestors chain.
+ #
+ # ==== Returns
+ # Array[Thor::Argument]
+ #
+ def arguments
+ @arguments ||= from_superclass(:arguments, [])
+ end
+
+ # Adds a bunch of options to the set of class options.
+ #
+ # class_options :foo => false, :bar => :required, :baz => :string
+ #
+ # If you prefer more detailed declaration, check class_option.
+ #
+ # ==== Parameters
+ # Hash[Symbol => Object]
+ #
+ def class_options(options=nil)
+ @class_options ||= from_superclass(:class_options, {})
+ build_options(options, @class_options) if options
+ @class_options
+ end
+
+ # Adds an option to the set of class options
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described below.
+ #
+ # ==== Options
+ # :desc - Description for the argument.
+ # :required - If the argument is required or not.
+ # :default - Default value for this argument.
+ # :group - The group for this options. Use by class options to output options in different levels.
+ # :aliases - Aliases for this option.
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
+ # :banner - String to show on usage notes.
+ #
+ def class_option(name, options={})
+ build_option(name, options, class_options)
+ end
+
+ # Removes a previous defined argument. If :undefine is given, undefine
+ # accessors as well.
+ #
+ # ==== Paremeters
+ # names<Array>:: Arguments to be removed
+ #
+ # ==== Examples
+ #
+ # remove_argument :foo
+ # remove_argument :foo, :bar, :baz, :undefine => true
+ #
+ def remove_argument(*names)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+
+ names.each do |name|
+ arguments.delete_if { |a| a.name == name.to_s }
+ undef_method name, "#{name}=" if options[:undefine]
+ end
+ end
+
+ # Removes a previous defined class option.
+ #
+ # ==== Paremeters
+ # names<Array>:: Class options to be removed
+ #
+ # ==== Examples
+ #
+ # remove_class_option :foo
+ # remove_class_option :foo, :bar, :baz
+ #
+ def remove_class_option(*names)
+ names.each do |name|
+ class_options.delete(name)
+ end
+ end
+
+ # Defines the group. This is used when thor list is invoked so you can specify
+ # that only tasks from a pre-defined group will be shown. Defaults to standard.
+ #
+ # ==== Parameters
+ # name<String|Symbol>
+ #
+ def group(name=nil)
+ case name
+ when nil
+ @group ||= from_superclass(:group, 'standard')
+ else
+ @group = name.to_s
+ end
+ end
+
+ # Returns the tasks for this Thor class.
+ #
+ # ==== Returns
+ # OrderedHash:: An ordered hash with this class tasks.
+ #
+ def tasks
+ @tasks ||= Thor::CoreExt::OrderedHash.new
+ end
+
+ # Returns the tasks for this Thor class and all subclasses.
+ #
+ # ==== Returns
+ # OrderedHash
+ #
+ def all_tasks
+ @all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new)
+ @all_tasks.merge(tasks)
+ end
+
+ # Removes a given task from this Thor class. This is usually done if you
+ # are inheriting from another class and don't want it to be available
+ # anymore.
+ #
+ # By default it only remove the mapping to the task. But you can supply
+ # :undefine => true to undefine the method from the class as well.
+ #
+ # ==== Parameters
+ # name<Symbol|String>:: The name of the task to be removed
+ # options<Hash>:: You can give :undefine => true if you want tasks the method
+ # to be undefined from the class as well.
+ #
+ def remove_task(*names)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+
+ names.each do |name|
+ tasks.delete(name.to_s)
+ all_tasks.delete(name.to_s)
+ undef_method name if options[:undefine]
+ end
+ end
+
+ # All methods defined inside the given block are not added as tasks.
+ #
+ # So you can do:
+ #
+ # class MyScript < Thor
+ # no_tasks do
+ # def this_is_not_a_task
+ # end
+ # end
+ # end
+ #
+ # You can also add the method and remove it from the task list:
+ #
+ # class MyScript < Thor
+ # def this_is_not_a_task
+ # end
+ # remove_task :this_is_not_a_task
+ # end
+ #
+ def no_tasks
+ @no_tasks = true
+ yield
+ @no_tasks = false
+ end
+
+ # Sets the namespace for the Thor or Thor::Group class. By default the
+ # namespace is retrieved from the class name. If your Thor class is named
+ # Scripts::MyScript, the help method, for example, will be called as:
+ #
+ # thor scripts:my_script -h
+ #
+ # If you change the namespace:
+ #
+ # namespace :my_scripts
+ #
+ # You change how your tasks are invoked:
+ #
+ # thor my_scripts -h
+ #
+ # Finally, if you change your namespace to default:
+ #
+ # namespace :default
+ #
+ # Your tasks can be invoked with a shortcut. Instead of:
+ #
+ # thor :my_task
+ #
+ def namespace(name=nil)
+ case name
+ when nil
+ @namespace ||= Thor::Util.constant_to_namespace(self, false)
+ else
+ @namespace = name.to_s
+ end
+ end
+
+ # Default way to start generators from the command line.
+ #
+ def start(given_args=ARGV, config={}) #:nodoc:
+ config[:shell] ||= Thor::Base.shell.new
+ yield
+ rescue Thor::Error => e
+ if given_args.include?("--debug")
+ raise e
+ else
+ config[:shell].error e.message
+ end
+ end
+
+ protected
+
+ # Prints the class options per group. If an option does not belong to
+ # any group, it uses the ungrouped name value. This method provide to
+ # hooks to add extra options, one of them if the third argument called
+ # extra_group that should be a hash in the format :group => Array[Options].
+ #
+ # The second is by returning a lamda used to print values. The lambda
+ # requires two options: the group name and the array of options.
+ #
+ def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc:
+ groups = {}
+
+ class_options.each do |_, value|
+ groups[value.group] ||= []
+ groups[value.group] << value
+ end
+
+ printer = proc do |group_name, options|
+ list = []
+ padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
+
+ options.each do |option|
+ item = [ option.usage(padding) ]
+
+ item << if option.description
+ "# #{option.description}"
+ else
+ ""
+ end
+
+ list << item
+ list << [ "", "# Default: #{option.default}" ] if option.show_default?
+ end
+
+ unless list.empty?
+ if group_name
+ shell.say "#{group_name} options:"
+ else
+ shell.say "Options:"
+ end
+
+ shell.print_table(list, :ident => 2)
+ shell.say ""
+ end
+ end
+
+ # Deal with default group
+ global_options = groups.delete(nil) || []
+ printer.call(ungrouped_name, global_options) if global_options
+
+ # Print all others
+ groups = extra_group.merge(groups) if extra_group
+ groups.each(&printer)
+ printer
+ end
+
+ # Raises an error if the word given is a Thor reserved word.
+ #
+ def is_thor_reserved_word?(word, type)
+ return false unless THOR_RESERVED_WORDS.include?(word.to_s)
+ raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
+ end
+
+ # Build an option and adds it to the given scope.
+ #
+ # ==== Parameters
+ # name<Symbol>:: The name of the argument.
+ # options<Hash>:: Described in both class_option and method_option.
+ #
+ def build_option(name, options, scope)
+ scope[name] = Thor::Option.new(name, options[:desc], options[:required],
+ options[:type], options[:default], options[:banner],
+ options[:group], options[:aliases])
+ end
+
+ # Receives a hash of options, parse them and add to the scope. This is a
+ # fast way to set a bunch of options:
+ #
+ # build_options :foo => true, :bar => :required, :baz => :string
+ #
+ # ==== Parameters
+ # Hash[Symbol => Object]
+ #
+ def build_options(options, scope)
+ options.each do |key, value|
+ scope[key] = Thor::Option.parse(key, value)
+ end
+ end
+
+ # Finds a task with the given name. If the task belongs to the current
+ # class, just return it, otherwise dup it and add the fresh copy to the
+ # current task hash.
+ #
+ def find_and_refresh_task(name)
+ task = if task = tasks[name.to_s]
+ task
+ elsif task = all_tasks[name.to_s]
+ tasks[name.to_s] = task.clone
+ else
+ raise ArgumentError, "You supplied :for => #{name.inspect}, but the task #{name.inspect} could not be found."
+ end
+ end
+
+ # Everytime someone inherits from a Thor class, register the klass
+ # and file into baseclass.
+ #
+ def inherited(klass)
+ Thor::Base.register_klass_file(klass)
+ end
+
+ # Fire this callback whenever a method is added. Added methods are
+ # tracked as tasks if the requirements set by valid_task? are valid.
+ #
+ def method_added(meth)
+ meth = meth.to_s
+
+ if meth == "initialize"
+ initialize_added
+ return
+ end
+
+ # Return if it's not a public instance method
+ return unless public_instance_methods.include?(meth) ||
+ public_instance_methods.include?(meth.to_sym)
+
+ return if @no_tasks || !create_task(meth)
+
+ is_thor_reserved_word?(meth, :task)
+ Thor::Base.register_klass_file(self)
+ end
+
+ # Retrieves a value from superclass. If it reaches the baseclass,
+ # returns nil.
+ #
+ def from_superclass(method, default=nil)
+ if self == baseclass || !superclass.respond_to?(method, true)
+ default
+ else
+ value = superclass.send(method)
+ value.dup if value
+ end
+ end
+
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
+ # finishes.
+ def baseclass #:nodoc:
+ end
+
+ # SIGNATURE: Creates a new task if valid_task? is true. This method is
+ # called when a new method is added to the class.
+ def create_task(meth) #:nodoc:
+ end
+
+ # SIGNATURE: Defines behavior when the initialize method is added to the
+ # class.
+ def initialize_added #:nodoc:
+ end
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/hash_with_indifferent_access.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/hash_with_indifferent_access.rb
new file mode 100644
index 0000000000..3213961fe4
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/hash_with_indifferent_access.rb
@@ -0,0 +1,75 @@
+class Thor
+ module CoreExt
+
+ # A hash with indifferent access and magic predicates.
+ #
+ # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
+ #
+ # hash[:foo] #=> 'bar'
+ # hash['foo'] #=> 'bar'
+ # hash.foo? #=> true
+ #
+ class HashWithIndifferentAccess < ::Hash
+
+ def initialize(hash={})
+ super()
+ hash.each do |key, value|
+ self[convert_key(key)] = value
+ end
+ end
+
+ def [](key)
+ super(convert_key(key))
+ end
+
+ def []=(key, value)
+ super(convert_key(key), value)
+ end
+
+ def delete(key)
+ super(convert_key(key))
+ end
+
+ def values_at(*indices)
+ indices.collect { |key| self[convert_key(key)] }
+ end
+
+ def merge(other)
+ dup.merge!(other)
+ end
+
+ def merge!(other)
+ other.each do |key, value|
+ self[convert_key(key)] = value
+ end
+ self
+ end
+
+ protected
+
+ def convert_key(key)
+ key.is_a?(Symbol) ? key.to_s : key
+ end
+
+ # Magic predicates. For instance:
+ #
+ # options.force? # => !!options['force']
+ # options.shebang # => "/usr/lib/local/ruby"
+ # options.test_framework?(:rspec) # => options[:test_framework] == :rspec
+ #
+ def method_missing(method, *args, &block)
+ method = method.to_s
+ if method =~ /^(\w+)\?$/
+ if args.empty?
+ !!self[$1]
+ else
+ self[$1] == args.first
+ end
+ else
+ self[method]
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/ordered_hash.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/ordered_hash.rb
new file mode 100644
index 0000000000..5e4ad5609f
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/ordered_hash.rb
@@ -0,0 +1,102 @@
+require 'forwardable'
+
+class Thor #:nodoc:
+ module CoreExt #:nodoc:
+
+ if RUBY_VERSION >= '1.9'
+ class OrderedHash < ::Hash
+ end
+ else
+ # This class is based on the Ruby 1.9 ordered hashes.
+ #
+ # It keeps the semantics and most of the efficiency of normal hashes
+ # while also keeping track of the order in which elements were set.
+ #
+ class OrderedHash #:nodoc:
+ include Enumerable
+
+ Node = Struct.new(:key, :value, :next, :prev)
+
+ def initialize
+ @hash = {}
+ end
+
+ def [](key)
+ @hash[key] && @hash[key].value
+ end
+
+ def []=(key, value)
+ if node = @hash[key]
+ node.value = value
+ else
+ node = Node.new(key, value)
+
+ if @first.nil?
+ @first = @last = node
+ else
+ node.prev = @last
+ @last.next = node
+ @last = node
+ end
+ end
+
+ @hash[key] = node
+ value
+ end
+
+ def delete(key)
+ if node = @hash[key]
+ prev_node = node.prev
+ next_node = node.next
+
+ next_node.prev = prev_node if next_node
+ prev_node.next = next_node if prev_node
+
+ @first = next_node if @first == node
+ @last = prev_node if @last == node
+
+ value = node.value
+ end
+
+ @hash.delete(key)
+ value
+ end
+
+ def keys
+ self.map { |k, v| k }
+ end
+
+ def values
+ self.map { |k, v| v }
+ end
+
+ def each
+ return unless @first
+ yield [@first.key, @first.value]
+ node = @first
+ yield [node.key, node.value] while node = node.next
+ self
+ end
+
+ def merge(other)
+ hash = self.class.new
+
+ self.each do |key, value|
+ hash[key] = value
+ end
+
+ other.each do |key, value|
+ hash[key] = value
+ end
+
+ hash
+ end
+
+ def empty?
+ @hash.empty?
+ end
+ end
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/error.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/error.rb
new file mode 100644
index 0000000000..c846e9ce74
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/error.rb
@@ -0,0 +1,27 @@
+class Thor
+ # Thor::Error is raised when it's caused by the user invoking the task and
+ # only errors that inherit from it are rescued.
+ #
+ # So, for example, if the developer declares a required argument after an
+ # option, it should raise an ::ArgumentError and not ::Thor::ArgumentError,
+ # because it was caused by the developer and not the "final user".
+ #
+ class Error < StandardError #:nodoc:
+ end
+
+ # Raised when a task was not found.
+ #
+ class UndefinedTaskError < Error #:nodoc:
+ end
+
+ # Raised when a task was found, but not invoked properly.
+ #
+ class InvocationError < Error #:nodoc:
+ end
+
+ class RequiredArgumentMissingError < InvocationError #:nodoc:
+ end
+
+ class MalformattedArgumentError < InvocationError #:nodoc:
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/group.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/group.rb
new file mode 100644
index 0000000000..1be1c35ba5
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/group.rb
@@ -0,0 +1,263 @@
+# Thor has a special class called Thor::Group. The main difference to Thor class
+# is that it invokes all tasks at once. It also include some methods that allows
+# invocations to be done at the class method, which are not available to Thor
+# tasks.
+#
+class Thor::Group
+ class << self
+ # The descrition for this Thor::Group. If none is provided, but a source root
+ # exists, tries to find the USAGE one folder above it, otherwise searches
+ # in the superclass.
+ #
+ # ==== Parameters
+ # description<String>:: The description for this Thor::Group.
+ #
+ def desc(description=nil)
+ case description
+ when nil
+ @desc ||= from_superclass(:desc, nil)
+ else
+ @desc = description
+ end
+ end
+
+ # Start works differently in Thor::Group, it simply invokes all tasks
+ # inside the class.
+ #
+ def start(given_args=ARGV, config={})
+ super do
+ if Thor::HELP_MAPPINGS.include?(given_args.first)
+ help(config[:shell])
+ return
+ end
+
+ args, opts = Thor::Options.split(given_args)
+ new(args, opts, config).invoke
+ end
+ end
+
+ # Prints help information.
+ #
+ # ==== Options
+ # short:: When true, shows only usage.
+ #
+ def help(shell, options={})
+ if options[:short]
+ shell.say banner
+ else
+ shell.say "Usage:"
+ shell.say " #{banner}"
+ shell.say
+ class_options_help(shell)
+ shell.say self.desc if self.desc
+ end
+ end
+
+ # Stores invocations for this class merging with superclass values.
+ #
+ def invocations #:nodoc:
+ @invocations ||= from_superclass(:invocations, {})
+ end
+
+ # Stores invocation blocks used on invoke_from_option.
+ #
+ def invocation_blocks #:nodoc:
+ @invocation_blocks ||= from_superclass(:invocation_blocks, {})
+ end
+
+ # Invoke the given namespace or class given. It adds an instance
+ # method that will invoke the klass and task. You can give a block to
+ # configure how it will be invoked.
+ #
+ # The namespace/class given will have its options showed on the help
+ # usage. Check invoke_from_option for more information.
+ #
+ def invoke(*names, &block)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+ verbose = options.fetch(:verbose, :white)
+
+ names.each do |name|
+ invocations[name] = false
+ invocation_blocks[name] = block if block_given?
+
+ class_eval <<-METHOD, __FILE__, __LINE__
+ def _invoke_#{name.to_s.gsub(/\W/, '_')}
+ klass, task = self.class.prepare_for_invocation(nil, #{name.inspect})
+
+ if klass
+ say_status :invoke, #{name.inspect}, #{verbose.inspect}
+ block = self.class.invocation_blocks[#{name.inspect}]
+ _invoke_for_class_method klass, task, &block
+ else
+ say_status :error, %(#{name.inspect} [not found]), :red
+ end
+ end
+ METHOD
+ end
+ end
+
+ # Invoke a thor class based on the value supplied by the user to the
+ # given option named "name". A class option must be created before this
+ # method is invoked for each name given.
+ #
+ # ==== Examples
+ #
+ # class GemGenerator < Thor::Group
+ # class_option :test_framework, :type => :string
+ # invoke_from_option :test_framework
+ # end
+ #
+ # ==== Boolean options
+ #
+ # In some cases, you want to invoke a thor class if some option is true or
+ # false. This is automatically handled by invoke_from_option. Then the
+ # option name is used to invoke the generator.
+ #
+ # ==== Preparing for invocation
+ #
+ # In some cases you want to customize how a specified hook is going to be
+ # invoked. You can do that by overwriting the class method
+ # prepare_for_invocation. The class method must necessarily return a klass
+ # and an optional task.
+ #
+ # ==== Custom invocations
+ #
+ # You can also supply a block to customize how the option is giong to be
+ # invoked. The block receives two parameters, an instance of the current
+ # class and the klass to be invoked.
+ #
+ def invoke_from_option(*names, &block)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+ verbose = options.fetch(:verbose, :white)
+
+ names.each do |name|
+ unless class_options.key?(name)
+ raise ArgumentError, "You have to define the option #{name.inspect} " <<
+ "before setting invoke_from_option."
+ end
+
+ invocations[name] = true
+ invocation_blocks[name] = block if block_given?
+
+ class_eval <<-METHOD, __FILE__, __LINE__
+ def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
+ return unless options[#{name.inspect}]
+
+ value = options[#{name.inspect}]
+ value = #{name.inspect} if TrueClass === value
+ klass, task = self.class.prepare_for_invocation(#{name.inspect}, value)
+
+ if klass
+ say_status :invoke, value, #{verbose.inspect}
+ block = self.class.invocation_blocks[#{name.inspect}]
+ _invoke_for_class_method klass, task, &block
+ else
+ say_status :error, %(\#{value} [not found]), :red
+ end
+ end
+ METHOD
+ end
+ end
+
+ # Remove a previously added invocation.
+ #
+ # ==== Examples
+ #
+ # remove_invocation :test_framework
+ #
+ def remove_invocation(*names)
+ names.each do |name|
+ remove_task(name)
+ remove_class_option(name)
+ invocations.delete(name)
+ invocation_blocks.delete(name)
+ end
+ end
+
+ # Overwrite class options help to allow invoked generators options to be
+ # shown recursively when invoking a generator.
+ #
+ def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc:
+ group_options = {}
+
+ get_options_from_invocations(group_options, class_options) do |klass|
+ klass.send(:get_options_from_invocations, group_options, class_options)
+ end
+
+ group_options.merge!(extra_group) if extra_group
+ super(shell, ungrouped_name, group_options)
+ end
+
+ # Get invocations array and merge options from invocations. Those
+ # options are added to group_options hash. Options that already exists
+ # in base_options are not added twice.
+ #
+ def get_options_from_invocations(group_options, base_options) #:nodoc:
+ invocations.each do |name, from_option|
+ value = if from_option
+ option = class_options[name]
+ option.type == :boolean ? name : option.default
+ else
+ name
+ end
+ next unless value
+
+ klass, task = prepare_for_invocation(name, value)
+ next unless klass && klass.respond_to?(:class_options)
+
+ value = value.to_s
+ human_name = value.respond_to?(:classify) ? value.classify : value
+
+ group_options[human_name] ||= []
+ group_options[human_name] += klass.class_options.values.select do |option|
+ base_options[option.name.to_sym].nil? && option.group.nil? &&
+ !group_options.values.flatten.any? { |i| i.name == option.name }
+ end
+
+ yield klass if block_given?
+ end
+ end
+
+ protected
+
+ # The banner for this class. You can customize it if you are invoking the
+ # thor class by another means which is not the Thor::Runner.
+ #
+ def banner #:nodoc:
+ "#{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}"
+ end
+
+ def baseclass #:nodoc:
+ Thor::Group
+ end
+
+ def create_task(meth) #:nodoc:
+ tasks[meth.to_s] = Thor::Task.new(meth, nil, nil, nil)
+ true
+ end
+ end
+
+ include Thor::Base
+
+ protected
+
+ # Shortcut to invoke with padding and block handling. Use internally by
+ # invoke and invoke_from_option class methods.
+ #
+ def _invoke_for_class_method(klass, task=nil, *args, &block)
+ shell.padding += 1
+
+ result = if block_given?
+ if block.arity == 2
+ block.call(self, klass)
+ else
+ block.call(self, klass, task)
+ end
+ else
+ invoke klass, task, *args
+ end
+
+ shell.padding -= 1
+ result
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/invocation.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/invocation.rb
new file mode 100644
index 0000000000..34e7a4b911
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/invocation.rb
@@ -0,0 +1,172 @@
+class Thor
+ module Invocation
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # Prepare for class methods invocations. This method must return a klass to
+ # have the invoked class options showed in help messages in generators.
+ #
+ def prepare_for_invocation(key, name) #:nodoc:
+ case name
+ when Symbol, String
+ Thor::Util.namespace_to_thor_class(name.to_s, false)
+ else
+ name
+ end
+ end
+ end
+
+ # Make initializer aware of invocations and the initializer proc.
+ #
+ def initialize(args=[], options={}, config={}, &block) #:nodoc:
+ @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
+ @_initializer = [ args, options, config ]
+ super
+ end
+
+ # Receives a name and invokes it. The name can be a string (either "task" or
+ # "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task
+ # cannot be guessed by name, it can also be supplied as second argument.
+ #
+ # You can also supply the arguments, options and configuration values for
+ # the task to be invoked, if none is given, the same values used to
+ # initialize the invoker are used to initialize the invoked.
+ #
+ # ==== Examples
+ #
+ # class A < Thor
+ # def foo
+ # invoke :bar
+ # invoke "b:hello", ["José"]
+ # end
+ #
+ # def bar
+ # invoke "b:hello", ["José"]
+ # end
+ # end
+ #
+ # class B < Thor
+ # def hello(name)
+ # puts "hello #{name}"
+ # end
+ # end
+ #
+ # You can notice that the method "foo" above invokes two tasks: "bar",
+ # which belongs to the same class and "hello" which belongs to the class B.
+ #
+ # By using an invocation system you ensure that a task is invoked only once.
+ # In the example above, invoking "foo" will invoke "b:hello" just once, even
+ # if it's invoked later by "bar" method.
+ #
+ # When class A invokes class B, all arguments used on A initialization are
+ # supplied to B. This allows lazy parse of options. Let's suppose you have
+ # some rspec tasks:
+ #
+ # class Rspec < Thor::Group
+ # class_option :mock_framework, :type => :string, :default => :rr
+ #
+ # def invoke_mock_framework
+ # invoke "rspec:#{options[:mock_framework]}"
+ # end
+ # end
+ #
+ # As you noticed, it invokes the given mock framework, which might have its
+ # own options:
+ #
+ # class Rspec::RR < Thor::Group
+ # class_option :style, :type => :string, :default => :mock
+ # end
+ #
+ # Since it's not rspec concern to parse mock framework options, when RR
+ # is invoked all options are parsed again, so RR can extract only the options
+ # that it's going to use.
+ #
+ # If you want Rspec::RR to be initialized with its own set of options, you
+ # have to do that explicitely:
+ #
+ # invoke "rspec:rr", [], :style => :foo
+ #
+ # Besides giving an instance, you can also give a class to invoke:
+ #
+ # invoke Rspec::RR, [], :style => :foo
+ #
+ def invoke(name=nil, task=nil, args=nil, opts=nil, config=nil)
+ task, args, opts, config = nil, task, args, opts if task.nil? || task.is_a?(Array)
+ args, opts, config = nil, args, opts if args.is_a?(Hash)
+
+ object, task = _prepare_for_invocation(name, task)
+ if object.is_a?(Class)
+ klass = object
+
+ stored_args, stored_opts, stored_config = @_initializer
+ args ||= stored_args.dup
+ opts ||= stored_opts.dup
+
+ config ||= {}
+ config = stored_config.merge(_shared_configuration).merge!(config)
+ instance = klass.new(args, opts, config)
+ else
+ klass, instance = object.class, object
+ end
+
+ method_args = []
+ current = @_invocations[klass]
+
+ iterator = proc do |_, task|
+ unless current.include?(task.name)
+ current << task.name
+ task.run(instance, method_args)
+ end
+ end
+
+ if task
+ args ||= []
+ method_args = args[Range.new(klass.arguments.size, -1)] || []
+ iterator.call(nil, task)
+ else
+ klass.all_tasks.map(&iterator)
+ end
+ end
+
+ protected
+
+ # Configuration values that are shared between invocations.
+ #
+ def _shared_configuration
+ { :invocations => @_invocations }
+ end
+
+ # Prepare for invocation in the instance level. In this case, we have to
+ # take into account that a just a task name from the current class was
+ # given or even a Thor::Task object.
+ #
+ def _prepare_for_invocation(name, sent_task=nil) #:nodoc:
+ if name.is_a?(Thor::Task)
+ task = name
+ elsif task = self.class.all_tasks[name.to_s]
+ object = self
+ else
+ object, task = self.class.prepare_for_invocation(nil, name)
+ task ||= sent_task
+ end
+
+ # If the object was not set, use self and use the name as task.
+ object, task = self, name unless object
+ return object, _validate_klass_and_task(object, task)
+ end
+
+ # Check if the object given is a Thor class object and get a task object
+ # for it.
+ #
+ def _validate_klass_and_task(object, task) #:nodoc:
+ klass = object.is_a?(Class) ? object : object.class
+ raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
+
+ task ||= klass.default_task if klass <= Thor
+ task = klass.all_tasks[task.to_s] || Task.dynamic(task) if task && !task.is_a?(Thor::Task)
+ task
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser.rb
new file mode 100644
index 0000000000..57a3f6e1a5
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser.rb
@@ -0,0 +1,4 @@
+require 'thor/parser/argument'
+require 'thor/parser/arguments'
+require 'thor/parser/option'
+require 'thor/parser/options'
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/argument.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/argument.rb
new file mode 100644
index 0000000000..2d7f4dbafb
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/argument.rb
@@ -0,0 +1,67 @@
+class Thor
+ class Argument
+ VALID_TYPES = [ :numeric, :hash, :array, :string ]
+
+ attr_reader :name, :description, :required, :type, :default, :banner
+ alias :human_name :name
+
+ def initialize(name, description=nil, required=true, type=:string, default=nil, banner=nil)
+ class_name = self.class.name.split("::").last
+
+ raise ArgumentError, "#{class_name} name can't be nil." if name.nil?
+ raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
+
+ @name = name.to_s
+ @description = description
+ @required = required || false
+ @type = (type || :string).to_sym
+ @default = default
+ @banner = banner || default_banner
+
+ validate! # Trigger specific validations
+ end
+
+ def usage
+ required? ? banner : "[#{banner}]"
+ end
+
+ def required?
+ required
+ end
+
+ def show_default?
+ case default
+ when Array, String, Hash
+ !default.empty?
+ else
+ default
+ end
+ end
+
+ protected
+
+ def validate!
+ raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
+ end
+
+ def valid_type?(type)
+ VALID_TYPES.include?(type.to_sym)
+ end
+
+ def default_banner
+ case type
+ when :boolean
+ nil
+ when :string, :default
+ human_name.upcase
+ when :numeric
+ "N"
+ when :hash
+ "key:value"
+ when :array
+ "one two three"
+ end
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/arguments.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/arguments.rb
new file mode 100644
index 0000000000..9a2262d6f7
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/arguments.rb
@@ -0,0 +1,145 @@
+class Thor
+ class Arguments
+ NUMERIC = /(\d*\.\d+|\d+)/
+
+ # Receives an array of args and returns two arrays, one with arguments
+ # and one with switches.
+ #
+ def self.split(args)
+ arguments = []
+
+ args.each do |item|
+ break if item =~ /^-/
+ arguments << item
+ end
+
+ return arguments, args[Range.new(arguments.size, -1)]
+ end
+
+ def self.parse(base, args)
+ new(base).parse(args)
+ end
+
+ # Takes an array of Thor::Argument objects.
+ #
+ def initialize(arguments=[])
+ @assigns, @non_assigned_required = {}, []
+ @switches = arguments
+
+ arguments.each do |argument|
+ if argument.default
+ @assigns[argument.human_name] = argument.default
+ elsif argument.required?
+ @non_assigned_required << argument
+ end
+ end
+ end
+
+ def parse(args)
+ @pile = args.dup
+
+ @switches.each do |argument|
+ break unless peek
+ @non_assigned_required.delete(argument)
+ @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name)
+ end
+
+ check_requirement!
+ @assigns
+ end
+
+ private
+
+ def peek
+ @pile.first
+ end
+
+ def shift
+ @pile.shift
+ end
+
+ def unshift(arg)
+ unless arg.kind_of?(Array)
+ @pile.unshift(arg)
+ else
+ @pile = arg + @pile
+ end
+ end
+
+ def current_is_value?
+ peek && peek.to_s !~ /^-/
+ end
+
+ # Runs through the argument array getting strings that contains ":" and
+ # mark it as a hash:
+ #
+ # [ "name:string", "age:integer" ]
+ #
+ # Becomes:
+ #
+ # { "name" => "string", "age" => "integer" }
+ #
+ def parse_hash(name)
+ return shift if peek.is_a?(Hash)
+ hash = {}
+
+ while current_is_value? && peek.include?(?:)
+ key, value = shift.split(':')
+ hash[key] = value
+ end
+ hash
+ end
+
+ # Runs through the argument array getting all strings until no string is
+ # found or a switch is found.
+ #
+ # ["a", "b", "c"]
+ #
+ # And returns it as an array:
+ #
+ # ["a", "b", "c"]
+ #
+ def parse_array(name)
+ return shift if peek.is_a?(Array)
+ array = []
+
+ while current_is_value?
+ array << shift
+ end
+ array
+ end
+
+ # Check if the peel is numeric ofrmat and return a Float or Integer.
+ # Otherwise raises an error.
+ #
+ def parse_numeric(name)
+ return shift if peek.is_a?(Numeric)
+
+ unless peek =~ NUMERIC && $& == peek
+ raise MalformattedArgumentError, "expected numeric value for '#{name}'; got #{peek.inspect}"
+ end
+
+ $&.index('.') ? shift.to_f : shift.to_i
+ end
+
+ # Parse string, i.e., just return the current value in the pile.
+ #
+ def parse_string(name)
+ shift
+ end
+
+ # Raises an error if @non_assigned_required array is not empty.
+ #
+ def check_requirement!
+ unless @non_assigned_required.empty?
+ names = @non_assigned_required.map do |o|
+ o.respond_to?(:switch_name) ? o.switch_name : o.human_name
+ end.join("', '")
+
+ class_name = self.class.name.split('::').last.downcase
+ raise RequiredArgumentMissingError, "no value provided for required #{class_name} '#{names}'"
+ end
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/option.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/option.rb
new file mode 100644
index 0000000000..5c43f6b18f
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/option.rb
@@ -0,0 +1,132 @@
+class Thor
+ class Option < Argument
+ attr_reader :aliases, :group
+
+ VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
+
+ def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil)
+ super(name, description, required, type, default, banner)
+ @aliases = [*aliases].compact
+ @group = group.to_s.capitalize if group
+ end
+
+ # This parse quick options given as method_options. It makes several
+ # assumptions, but you can be more specific using the option method.
+ #
+ # parse :foo => "bar"
+ # #=> Option foo with default value bar
+ #
+ # parse [:foo, :baz] => "bar"
+ # #=> Option foo with default value bar and alias :baz
+ #
+ # parse :foo => :required
+ # #=> Required option foo without default value
+ #
+ # parse :foo => 2
+ # #=> Option foo with default value 2 and type numeric
+ #
+ # parse :foo => :numeric
+ # #=> Option foo without default value and type numeric
+ #
+ # parse :foo => true
+ # #=> Option foo with default value true and type boolean
+ #
+ # The valid types are :boolean, :numeric, :hash, :array and :string. If none
+ # is given a default type is assumed. This default type accepts arguments as
+ # string (--foo=value) or booleans (just --foo).
+ #
+ # By default all options are optional, unless :required is given.
+ #
+ def self.parse(key, value)
+ if key.is_a?(Array)
+ name, *aliases = key
+ else
+ name, aliases = key, []
+ end
+
+ name = name.to_s
+ default = value
+
+ type = case value
+ when Symbol
+ default = nil
+
+ if VALID_TYPES.include?(value)
+ value
+ elsif required = (value == :required)
+ :string
+ elsif value == :optional
+ # TODO Remove this warning in the future.
+ warn "Optional type is deprecated. Choose :boolean or :string instead. Assumed to be :boolean."
+ :boolean
+ end
+ when TrueClass, FalseClass
+ :boolean
+ when Numeric
+ :numeric
+ when Hash, Array, String
+ value.class.name.downcase.to_sym
+ end
+
+ self.new(name.to_s, nil, required, type, default, nil, nil, aliases)
+ end
+
+ def switch_name
+ @switch_name ||= dasherized? ? name : dasherize(name)
+ end
+
+ def human_name
+ @human_name ||= dasherized? ? undasherize(name) : name
+ end
+
+ def usage(padding=0)
+ sample = if banner && !banner.to_s.empty?
+ "#{switch_name}=#{banner}"
+ else
+ switch_name
+ end
+
+ sample = "[#{sample}]" unless required?
+
+ if aliases.empty?
+ (" " * padding) << sample
+ else
+ "#{aliases.join(', ')}, #{sample}"
+ end
+ end
+
+ # Allow some type predicates as: boolean?, string? and etc.
+ #
+ def method_missing(method, *args, &block)
+ given = method.to_s.sub(/\?$/, '').to_sym
+ if valid_type?(given)
+ self.type == given
+ else
+ super
+ end
+ end
+
+ protected
+
+ def validate!
+ raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
+ end
+
+ def valid_type?(type)
+ VALID_TYPES.include?(type.to_sym)
+ end
+
+ def dasherized?
+ name.index('-') == 0
+ end
+
+ def undasherize(str)
+ str.sub(/^-{1,2}/, '')
+ end
+
+ def dasherize(str)
+ (str.length > 1 ? "--" : "-") + str.gsub('_', '-')
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/options.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/options.rb
new file mode 100644
index 0000000000..01c86b7b27
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/options.rb
@@ -0,0 +1,142 @@
+class Thor
+ # This is a modified version of Daniel Berger's Getopt::Long class, licensed
+ # under Ruby's license.
+ #
+ class Options < Arguments
+ LONG_RE = /^(--\w+[-\w+]*)$/
+ SHORT_RE = /^(-[a-z])$/i
+ EQ_RE = /^(--\w+[-\w+]*|-[a-z])=(.*)$/i
+ SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args
+ SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
+
+ # Receives a hash and makes it switches.
+ #
+ def self.to_switches(options)
+ options.map do |key, value|
+ case value
+ when true
+ "--#{key}"
+ when Array
+ "--#{key} #{value.map{ |v| v.inspect }.join(' ')}"
+ when Hash
+ "--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}"
+ when nil, false
+ ""
+ else
+ "--#{key} #{value.inspect}"
+ end
+ end.join(" ")
+ end
+
+ # Takes a hash of Thor::Option objects.
+ #
+ def initialize(options={})
+ options = options.values
+ super(options)
+ @shorts, @switches = {}, {}
+
+ options.each do |option|
+ @switches[option.switch_name] = option
+
+ option.aliases.each do |short|
+ @shorts[short.to_s] ||= option.switch_name
+ end
+ end
+ end
+
+ def parse(args)
+ @pile = args.dup
+
+ while peek
+ if current_is_switch?
+ case shift
+ when SHORT_SQ_RE
+ unshift($1.split('').map { |f| "-#{f}" })
+ next
+ when EQ_RE, SHORT_NUM
+ unshift($2)
+ switch = $1
+ when LONG_RE, SHORT_RE
+ switch = $1
+ end
+
+ switch = normalize_switch(switch)
+ next unless option = switch_option(switch)
+
+ @assigns[option.human_name] = parse_peek(switch, option)
+ else
+ shift
+ end
+ end
+
+ check_requirement!
+ @assigns
+ end
+
+ protected
+
+ # Returns true if the current value in peek is a registered switch.
+ #
+ def current_is_switch?
+ case peek
+ when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
+ switch?($1)
+ when SHORT_SQ_RE
+ $1.split('').any? { |f| switch?("-#{f}") }
+ end
+ end
+
+ def switch?(arg)
+ switch_option(arg) || @shorts.key?(arg)
+ end
+
+ def switch_option(arg)
+ if match = no_or_skip?(arg)
+ @switches[arg] || @switches["--#{match}"]
+ else
+ @switches[arg]
+ end
+ end
+
+ def no_or_skip?(arg)
+ arg =~ /^--(no|skip)-([-\w]+)$/
+ $2
+ end
+
+ # Check if the given argument is actually a shortcut.
+ #
+ def normalize_switch(arg)
+ @shorts.key?(arg) ? @shorts[arg] : arg
+ end
+
+ # Parse boolean values which can be given as --foo=true, --foo or --no-foo.
+ #
+ def parse_boolean(switch)
+ if current_is_value?
+ ["true", "TRUE", "t", "T", true].include?(shift)
+ else
+ @switches.key?(switch) || !no_or_skip?(switch)
+ end
+ end
+
+ # Parse the value at the peek analyzing if it requires an input or not.
+ #
+ def parse_peek(switch, option)
+ unless current_is_value?
+ if option.boolean?
+ # No problem for boolean types
+ elsif no_or_skip?(switch)
+ return nil # User set value to nil
+ elsif option.string? && !option.required?
+ return option.human_name # Return the option name
+ else
+ raise MalformattedArgumentError, "no value provided for option '#{switch}'"
+ end
+ end
+
+ @non_assigned_required.delete(option)
+ send(:"parse_#{option.type}", switch)
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/runner.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/runner.rb
new file mode 100644
index 0000000000..6782c61dec
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/runner.rb
@@ -0,0 +1,291 @@
+require 'fileutils'
+require 'open-uri'
+require 'yaml'
+require 'digest/md5'
+require 'pathname'
+
+class Thor::Runner < Thor
+ map "-T" => :list, "-i" => :install, "-u" => :update
+
+ # Override Thor#help so it can give information about any class and any method.
+ #
+ def help(meth=nil)
+ if meth && !self.respond_to?(meth)
+ initialize_thorfiles(meth)
+ klass, task = Thor::Util.namespace_to_thor_class(meth)
+ klass.start(["-h", task].compact, :shell => self.shell) # send mapping -h because it works with Thor::Group too
+ else
+ super
+ end
+ end
+
+ # If a task is not found on Thor::Runner, method missing is invoked and
+ # Thor::Runner is then responsable for finding the task in all classes.
+ #
+ def method_missing(meth, *args)
+ meth = meth.to_s
+ initialize_thorfiles(meth)
+ klass, task = Thor::Util.namespace_to_thor_class(meth)
+ args.unshift(task) if task
+ klass.start(args, :shell => shell)
+ end
+
+ desc "install NAME", "Install a Thor file into your system tasks, optionally named for future updates"
+ method_options :as => :string, :relative => :boolean
+ def install(name)
+ initialize_thorfiles
+
+ # If a directory name is provided as the argument, look for a 'main.thor'
+ # task in said directory.
+ begin
+ if File.directory?(File.expand_path(name))
+ base, package = File.join(name, "main.thor"), :directory
+ contents = open(base).read
+ else
+ base, package = name, :file
+ contents = open(name).read
+ end
+ rescue OpenURI::HTTPError
+ raise Error, "Error opening URI '#{name}'"
+ rescue Errno::ENOENT
+ raise Error, "Error opening file '#{name}'"
+ end
+
+ say "Your Thorfile contains:"
+ say contents
+
+ return false if no?("Do you wish to continue [y/N]?")
+
+ as = options["as"] || begin
+ first_line = contents.split("\n")[0]
+ (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil
+ end
+
+ unless as
+ basename = File.basename(name)
+ as = ask("Please specify a name for #{name} in the system repository [#{basename}]:")
+ as = basename if as.empty?
+ end
+
+ location = if options[:relative] || name =~ /^http:\/\//
+ name
+ else
+ File.expand_path(name)
+ end
+
+ thor_yaml[as] = {
+ :filename => Digest::MD5.hexdigest(name + as),
+ :location => location,
+ :namespaces => Thor::Util.namespaces_in_contents(contents, base)
+ }
+
+ save_yaml(thor_yaml)
+ say "Storing thor file in your system repository"
+ destination = File.join(thor_root, thor_yaml[as][:filename])
+
+ if package == :file
+ File.open(destination, "w") { |f| f.puts contents }
+ else
+ FileUtils.cp_r(name, destination)
+ end
+
+ thor_yaml[as][:filename] # Indicate success
+ end
+
+ desc "uninstall NAME", "Uninstall a named Thor module"
+ def uninstall(name)
+ raise Error, "Can't find module '#{name}'" unless thor_yaml[name]
+ say "Uninstalling #{name}."
+ FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}"))
+
+ thor_yaml.delete(name)
+ save_yaml(thor_yaml)
+
+ puts "Done."
+ end
+
+ desc "update NAME", "Update a Thor file from its original location"
+ def update(name)
+ raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location]
+
+ say "Updating '#{name}' from #{thor_yaml[name][:location]}"
+
+ old_filename = thor_yaml[name][:filename]
+ self.options = self.options.merge("as" => name)
+ filename = install(thor_yaml[name][:location])
+
+ unless filename == old_filename
+ File.delete(File.join(thor_root, old_filename))
+ end
+ end
+
+ desc "installed", "List the installed Thor modules and tasks"
+ method_options :internal => :boolean
+ def installed
+ initialize_thorfiles(nil, true)
+
+ klasses = Thor::Base.subclasses
+ klasses -= [Thor, Thor::Runner] unless options["internal"]
+
+ display_klasses(true, klasses)
+ end
+
+ desc "list [SEARCH]",
+ "List the available thor tasks (--substring means SEARCH anywhere in the namespace)"
+ method_options :substring => :boolean, :group => :string, :all => :boolean
+ def list(search="")
+ initialize_thorfiles
+
+ search = ".*#{search}" if options["substring"]
+ search = /^#{search}.*/i
+ group = options[:group] || "standard"
+
+ klasses = Thor::Base.subclasses.select do |k|
+ (options[:all] || k.group == group) && k.namespace =~ search
+ end
+
+ display_klasses(false, klasses)
+ end
+
+ private
+
+ def thor_root
+ Thor::Util.thor_root
+ end
+
+ def thor_yaml
+ @thor_yaml ||= begin
+ yaml_file = File.join(thor_root, "thor.yml")
+ yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
+ yaml || {}
+ end
+ end
+
+ # Save the yaml file. If none exists in thor root, creates one.
+ #
+ def save_yaml(yaml)
+ yaml_file = File.join(thor_root, "thor.yml")
+
+ unless File.exists?(yaml_file)
+ FileUtils.mkdir_p(thor_root)
+ yaml_file = File.join(thor_root, "thor.yml")
+ FileUtils.touch(yaml_file)
+ end
+
+ File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
+ end
+
+ # Load the thorfiles. If relevant_to is supplied, looks for specific files
+ # in the thor_root instead of loading them all.
+ #
+ # By default, it also traverses the current path until find Thor files, as
+ # described in thorfiles. This look up can be skipped by suppliying
+ # skip_lookup true.
+ #
+ def initialize_thorfiles(relevant_to=nil, skip_lookup=false)
+ thorfiles(relevant_to, skip_lookup).each do |f|
+ Thor::Util.load_thorfile(f) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f))
+ end
+ end
+
+ # Finds Thorfiles by traversing from your current directory down to the root
+ # directory of your system. If at any time we find a Thor file, we stop.
+ #
+ # We also ensure that system-wide Thorfiles are loaded first, so local
+ # Thorfiles can override them.
+ #
+ # ==== Example
+ #
+ # If we start at /Users/wycats/dev/thor ...
+ #
+ # 1. /Users/wycats/dev/thor
+ # 2. /Users/wycats/dev
+ # 3. /Users/wycats <-- we find a Thorfile here, so we stop
+ #
+ # Suppose we start at c:\Documents and Settings\james\dev\thor ...
+ #
+ # 1. c:\Documents and Settings\james\dev\thor
+ # 2. c:\Documents and Settings\james\dev
+ # 3. c:\Documents and Settings\james
+ # 4. c:\Documents and Settings
+ # 5. c:\ <-- no Thorfiles found!
+ #
+ def thorfiles(relevant_to=nil, skip_lookup=false)
+ # Deal with deprecated thor when :namespaces: is available as constants
+ save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml)
+
+ thorfiles = []
+
+ unless skip_lookup
+ Pathname.pwd.ascend do |path|
+ thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
+ break unless thorfiles.empty?
+ end
+ end
+
+ files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob)
+ files += thorfiles
+ files -= ["#{thor_root}/thor.yml"]
+
+ files.map! do |file|
+ File.directory?(file) ? File.join(file, "main.thor") : file
+ end
+ end
+
+ # Load thorfiles relevant to the given method. If you provide "foo:bar" it
+ # will load all thor files in the thor.yaml that has "foo" e "foo:bar"
+ # namespaces registered.
+ #
+ def thorfiles_relevant_to(meth)
+ lookup = [ meth, meth.split(":")[0...-1].join(":") ]
+
+ files = thor_yaml.select do |k, v|
+ v[:namespaces] && !(v[:namespaces] & lookup).empty?
+ end
+
+ files.map { |k, v| File.join(thor_root, "#{v[:filename]}") }
+ end
+
+ # Display information about the given klasses. If with_module is given,
+ # it shows a table with information extracted from the yaml file.
+ #
+ def display_klasses(with_modules=false, klasses=Thor.subclasses)
+ klasses -= [Thor, Thor::Runner] unless with_modules
+ raise Error, "No Thor tasks available" if klasses.empty?
+
+ if with_modules && !thor_yaml.empty?
+ info = []
+ labels = ["Modules", "Namespaces"]
+
+ info << labels
+ info << [ "-" * labels[0].size, "-" * labels[1].size ]
+
+ thor_yaml.each do |name, hash|
+ info << [ name, hash[:namespaces].join(", ") ]
+ end
+
+ print_table info
+ say ""
+ end
+
+ unless klasses.empty?
+ klasses.each { |k| display_tasks(k) }
+ else
+ say "\033[1;34mNo Thor tasks available\033[0m"
+ end
+ end
+
+ # Display tasks from the given Thor class.
+ #
+ def display_tasks(klass)
+ unless klass.tasks.empty?
+ base = klass.namespace
+
+ color = base == "default" ? :magenta : :blue
+ say shell.set_color(base, color, true)
+ say "-" * base.length
+
+ klass.help(shell, :short => true, :namespace => true)
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/shell.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/shell.rb
new file mode 100644
index 0000000000..7ed4a24bfb
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/shell.rb
@@ -0,0 +1,72 @@
+require 'thor/shell/color'
+
+class Thor
+ module Base
+ # Returns the shell used in all Thor classes. Default to color one.
+ #
+ def self.shell
+ @shell ||= Thor::Shell::Color
+ end
+
+ # Sets the shell used in all Thor classes.
+ #
+ def self.shell=(klass)
+ @shell = klass
+ end
+ end
+
+ module Shell
+ SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table]
+
+ # Add shell to initialize config values.
+ #
+ # ==== Configuration
+ # shell<Object>:: An instance of the shell to be used.
+ #
+ # ==== Examples
+ #
+ # class MyScript < Thor
+ # argument :first, :type => :numeric
+ # end
+ #
+ # MyScript.new [1.0], { :foo => :bar }, :shell => Thor::Shell::Basic.new
+ #
+ def initialize(args=[], options={}, config={})
+ super
+ self.shell = config[:shell]
+ self.shell.base ||= self if self.shell.respond_to?(:base)
+ end
+
+ # Holds the shell for the given Thor instance. If no shell is given,
+ # it gets a default shell from Thor::Base.shell.
+ #
+ def shell
+ @shell ||= Thor::Base.shell.new
+ end
+
+ # Sets the shell for this thor class.
+ #
+ def shell=(shell)
+ @shell = shell
+ end
+
+ # Common methods that are delegated to the shell.
+ #
+ SHELL_DELEGATED_METHODS.each do |method|
+ module_eval <<-METHOD, __FILE__, __LINE__
+ def #{method}(*args)
+ shell.#{method}(*args)
+ end
+ METHOD
+ end
+
+ protected
+
+ # Allow shell to be shared between invocations.
+ #
+ def _shared_configuration
+ super.merge!(:shell => self.shell)
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/shell/basic.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/basic.rb
new file mode 100644
index 0000000000..e294c87567
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/basic.rb
@@ -0,0 +1,216 @@
+require 'tempfile'
+
+class Thor
+ module Shell
+ class Basic
+ attr_accessor :base, :padding
+
+ # Initialize base and padding to nil.
+ #
+ def initialize #:nodoc:
+ @base, @padding = nil, 0
+ end
+
+ # Do not allow padding to be less than zero.
+ #
+ def padding=(value) #:nodoc:
+ @padding = [0, value].max
+ end
+
+ # Ask something to the user and receives a response.
+ #
+ # ==== Example
+ # ask("What is your name?")
+ #
+ def ask(statement, color=nil)
+ say("#{statement} ", color)
+ $stdin.gets.strip
+ end
+
+ # Say (print) something to the user. If the sentence ends with a whitespace
+ # or tab character, a new line is not appended (print + flush). Otherwise
+ # are passed straight to puts (behavior got from Highline).
+ #
+ # ==== Example
+ # say("I know you knew that.")
+ #
+ def say(message="", color=nil, force_new_line=false)
+ message = message.to_s
+ new_line = force_new_line || !(message[-1, 1] == " " || message[-1, 1] == "\t")
+ message = set_color(message, color) if color
+
+ if new_line
+ $stdout.puts(message)
+ else
+ $stdout.print(message)
+ $stdout.flush
+ end
+ end
+
+ # Say a status with the given color and appends the message. Since this
+ # method is used frequently by actions, it allows nil or false to be given
+ # in log_status, avoiding the message from being shown. If a Symbol is
+ # given in log_status, it's used as the color.
+ #
+ def say_status(status, message, log_status=true) #:nodoc:
+ return if quiet? || log_status == false
+ spaces = " " * (padding + 1)
+ color = log_status.is_a?(Symbol) ? log_status : :green
+
+ status = status.to_s.rjust(12)
+ status = set_color status, color, true if color
+ say "#{status}#{spaces}#{message}", nil, true
+ end
+
+ # Make a question the to user and returns true if the user replies "y" or
+ # "yes".
+ #
+ def yes?(statement, color=nil)
+ ask(statement, color) =~ is?(:yes)
+ end
+
+ # Make a question the to user and returns true if the user replies "n" or
+ # "no".
+ #
+ def no?(statement, color=nil)
+ !yes?(statement, color)
+ end
+
+ # Prints a list of items.
+ #
+ # ==== Parameters
+ # list<Array[String, String, ...]>
+ # mode<Symbol>:: Can be :rows or :inline. Defaults to :rows.
+ #
+ def print_list(list, mode=:rows)
+ return if list.empty?
+
+ content = case mode
+ when :inline
+ last = list.pop
+ "#{list.join(", ")}, and #{last}"
+ else # rows
+ list.join("\n")
+ end
+
+ $stdout.puts content
+ end
+
+ # Prints a table.
+ #
+ # ==== Parameters
+ # Array[Array[String, String, ...]]
+ #
+ # ==== Options
+ # ident<Integer>:: Ident the first column by ident value.
+ # emphasize_last<Boolean>:: When true, add a different behavior to the last column.
+ #
+ def print_table(table, options={})
+ return if table.empty?
+
+ formats = []
+ 0.upto(table.first.length - 2) do |i|
+ maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size
+ formats << "%-#{maxima + 2}s"
+ end
+
+ formats[0] = formats[0].insert(0, " " * options[:ident]) if options[:ident]
+ formats << "%s"
+
+ table.each do |row|
+ row.each_with_index do |column, i|
+ $stdout.print formats[i] % column.to_s
+ end
+ $stdout.puts
+ end
+ end
+
+ # Deals with file collision and returns true if the file should be
+ # overwriten and false otherwise. If a block is given, it uses the block
+ # response as the content for the diff.
+ #
+ # ==== Parameters
+ # destination<String>:: the destination file to solve conflicts
+ # block<Proc>:: an optional proc that returns the value to be used in diff
+ #
+ def file_collision(destination)
+ return true if @always_force
+ options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
+
+ while true
+ answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
+
+ case answer
+ when is?(:yes), is?(:force)
+ return true
+ when is?(:no), is?(:skip)
+ return false
+ when is?(:always)
+ return @always_force = true
+ when is?(:quit)
+ say 'Aborting...'
+ raise SystemExit
+ when is?(:diff)
+ show_diff(destination, yield) if block_given?
+ say 'Retrying...'
+ else
+ say file_collision_help
+ end
+ end
+ end
+
+ # Called if something goes wrong during the execution. This is used by Thor
+ # internally and should not be used inside your scripts. If someone went
+ # wrong, you can always raise an exception. If you raise a Thor::Error, it
+ # will be rescued and wrapped in the method below.
+ #
+ def error(statement) #:nodoc:
+ $stderr.puts statement
+ end
+
+ # Apply color to the given string with optional bold.
+ #
+ def set_color(string, color, bold=false)
+ string
+ end
+
+ protected
+
+ def is?(value)
+ value = value.to_s
+
+ if value.size == 1
+ /\A#{value}\z/i
+ else
+ /\A(#{value}|#{value[0,1]})\z/i
+ end
+ end
+
+ def file_collision_help
+<<HELP
+Y - yes, overwrite
+n - no, do not overwrite
+a - all, overwrite this and all others
+q - quit, abort
+d - diff, show the differences between the old and the new
+h - help, show this help
+HELP
+ end
+
+ def show_diff(destination, content)
+ diff_cmd = ENV['THOR_DIFF'] || ENV['RAILS_DIFF'] || 'diff -u'
+
+ Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
+ temp.write content
+ temp.rewind
+ say `#{diff_cmd} "#{destination}" "#{temp.path}"`
+ end
+ end
+
+ def quiet?
+ base && base.options[:quiet]
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/shell/color.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/color.rb
new file mode 100644
index 0000000000..be7995146a
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/color.rb
@@ -0,0 +1,106 @@
+require 'thor/shell/basic'
+
+class Thor
+ module Shell
+ # Set color in the output. Got color values from HighLine.
+ #
+ class Color < Basic
+ # Embed in a String to clear all previous ANSI sequences.
+ CLEAR = "\e[0m"
+ # The start of an ANSI bold sequence.
+ BOLD = "\e[1m"
+
+ # Set the terminal's foreground ANSI color to black.
+ BLACK = "\e[30m"
+ # Set the terminal's foreground ANSI color to red.
+ RED = "\e[31m"
+ # Set the terminal's foreground ANSI color to green.
+ GREEN = "\e[32m"
+ # Set the terminal's foreground ANSI color to yellow.
+ YELLOW = "\e[33m"
+ # Set the terminal's foreground ANSI color to blue.
+ BLUE = "\e[34m"
+ # Set the terminal's foreground ANSI color to magenta.
+ MAGENTA = "\e[35m"
+ # Set the terminal's foreground ANSI color to cyan.
+ CYAN = "\e[36m"
+ # Set the terminal's foreground ANSI color to white.
+ WHITE = "\e[37m"
+
+ # Set the terminal's background ANSI color to black.
+ ON_BLACK = "\e[40m"
+ # Set the terminal's background ANSI color to red.
+ ON_RED = "\e[41m"
+ # Set the terminal's background ANSI color to green.
+ ON_GREEN = "\e[42m"
+ # Set the terminal's background ANSI color to yellow.
+ ON_YELLOW = "\e[43m"
+ # Set the terminal's background ANSI color to blue.
+ ON_BLUE = "\e[44m"
+ # Set the terminal's background ANSI color to magenta.
+ ON_MAGENTA = "\e[45m"
+ # Set the terminal's background ANSI color to cyan.
+ ON_CYAN = "\e[46m"
+ # Set the terminal's background ANSI color to white.
+ ON_WHITE = "\e[47m"
+
+ # Set color by using a string or one of the defined constants. Based
+ # on Highline implementation. CLEAR is automatically be embedded to
+ # the end of the returned String.
+ #
+ def set_color(string, color, bold=false)
+ color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
+ bold = bold ? BOLD : ""
+ "#{bold}#{color}#{string}#{CLEAR}"
+ end
+
+ protected
+
+ # Overwrite show_diff to show diff with colors if Diff::LCS is
+ # available.
+ #
+ def show_diff(destination, content)
+ if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil?
+ actual = File.read(destination).to_s.split("\n")
+ content = content.to_s.split("\n")
+
+ Diff::LCS.sdiff(actual, content).each do |diff|
+ output_diff_line(diff)
+ end
+ else
+ super
+ end
+ end
+
+ def output_diff_line(diff)
+ case diff.action
+ when '-'
+ say "- #{diff.old_element.chomp}", :red, true
+ when '+'
+ say "+ #{diff.new_element.chomp}", :green, true
+ when '!'
+ say "- #{diff.old_element.chomp}", :red, true
+ say "+ #{diff.new_element.chomp}", :green, true
+ else
+ say " #{diff.old_element.chomp}", nil, true
+ end
+ end
+
+ # Check if Diff::LCS is loaded. If it is, use it to create pretty output
+ # for diff.
+ #
+ def diff_lcs_loaded?
+ return true if defined?(Diff::LCS)
+ return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
+
+ @diff_lcs_loaded = begin
+ require 'diff/lcs'
+ true
+ rescue LoadError
+ false
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/task.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/task.rb
new file mode 100644
index 0000000000..92c0776c04
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/task.rb
@@ -0,0 +1,108 @@
+class Thor
+ class Task < Struct.new(:name, :description, :usage, :options)
+
+ # Creates a dynamic task. Dynamic tasks are created on demand to allow method
+ # missing calls (since a method missing does not have a task object for it).
+ #
+ def self.dynamic(name)
+ new(name, "A dynamically-generated task", name.to_s)
+ end
+
+ def initialize(name, description, usage, options=nil)
+ super(name.to_s, description, usage, options || {})
+ end
+
+ def initialize_copy(other)
+ super(other)
+ self.options = other.options.dup if other.options
+ end
+
+ def short_description
+ description.split("\n").first if description
+ end
+
+ # By default, a task invokes a method in the thor class. You can change this
+ # implementation to create custom tasks.
+ #
+ def run(instance, args=[])
+ raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
+ instance.send(name, *args)
+ rescue ArgumentError => e
+ parse_argument_error(instance, e, caller)
+ rescue NoMethodError => e
+ parse_no_method_error(instance, e)
+ end
+
+ # Returns the formatted usage. If a class is given, the class arguments are
+ # injected in the usage.
+ #
+ def formatted_usage(klass=nil, namespace=false)
+ formatted = ''
+ formatted << "#{klass.namespace.gsub(/^default/,'')}:" if klass && namespace
+ formatted << formatted_arguments(klass)
+ formatted << " #{formatted_options}"
+ formatted.strip!
+ formatted
+ end
+
+ # Injects the class arguments into the task usage.
+ #
+ def formatted_arguments(klass)
+ if klass && !klass.arguments.empty?
+ usage.to_s.gsub(/^#{name}/) do |match|
+ match << " " << klass.arguments.map{ |a| a.usage }.join(' ')
+ end
+ else
+ usage.to_s
+ end
+ end
+
+ # Returns the options usage for this task.
+ #
+ def formatted_options
+ @formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ")
+ end
+
+ protected
+
+ # Given a target, checks if this class name is not a private/protected method.
+ #
+ def public_method?(instance)
+ collection = instance.private_methods + instance.protected_methods
+ !(collection).include?(name.to_s) && !(collection).include?(name.to_sym) # For Ruby 1.9
+ end
+
+ # Clean everything that comes from the Thor gempath and remove the caller.
+ #
+ def sans_backtrace(backtrace, caller)
+ dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/
+ saned = backtrace.reject { |frame| frame =~ dirname }
+ saned -= caller
+ end
+
+ def parse_argument_error(instance, e, caller)
+ backtrace = sans_backtrace(e.backtrace, caller)
+
+ if backtrace.empty? && e.message =~ /wrong number of arguments/
+ if instance.is_a?(Thor::Group)
+ raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
+ else
+ raise InvocationError, "'#{name}' was called incorrectly. Call as " <<
+ "'#{formatted_usage(instance.class, true)}'"
+ end
+ else
+ raise e
+ end
+ end
+
+ def parse_no_method_error(instance, e)
+ if e.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
+ raise UndefinedTaskError, "The #{instance.class.namespace} namespace " <<
+ "doesn't have a '#{name}' task"
+ else
+ raise e
+ end
+ end
+
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks.rb
new file mode 100644
index 0000000000..d1a7b1c673
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks.rb
@@ -0,0 +1,4 @@
+# This only loads all tasks inside tasks.
+Dir[File.join(File.dirname(__FILE__), "tasks", "*.rb")].each do |task|
+ require task
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/install.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/install.rb
new file mode 100644
index 0000000000..6b20ff1634
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/install.rb
@@ -0,0 +1,35 @@
+class Thor
+ # Creates an install task.
+ #
+ # ==== Parameters
+ # spec<Gem::Specification>
+ #
+ # ==== Options
+ # :dir - The directory where the package is hold before installation. Defaults to ./pkg.
+ #
+ def self.install_task(spec, options={})
+ package_task(spec, options)
+ tasks['install'] = Thor::InstallTask.new(spec, options)
+ end
+
+ class InstallTask < Task
+ attr_accessor :spec, :config
+
+ def initialize(gemspec, config={})
+ super(:install, "Install the gem", "install", {})
+ @spec = gemspec
+ @config = { :dir => File.join(Dir.pwd, "pkg") }.merge(config)
+ end
+
+ def run(instance, args=[])
+ null, sudo, gem = RUBY_PLATFORM =~ /mswin|mingw/ ? ['NUL', '', 'gem.bat'] :
+ ['/dev/null', 'sudo', 'gem']
+
+ old_stderr, $stderr = $stderr.dup, File.open(null, "w")
+ instance.invoke(:package)
+ $stderr = old_stderr
+
+ system %{#{sudo} #{Gem.ruby} -S #{gem} install #{config[:dir]}/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources}
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/package.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/package.rb
new file mode 100644
index 0000000000..603d61b4ab
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/package.rb
@@ -0,0 +1,31 @@
+require "fileutils"
+
+class Thor
+ # Creates a package task.
+ #
+ # ==== Parameters
+ # spec<Gem::Specification>
+ #
+ # ==== Options
+ # :dir - The package directory. Defaults to ./pkg.
+ #
+ def self.package_task(spec, options={})
+ tasks['package'] = Thor::PackageTask.new(spec, options)
+ end
+
+ class PackageTask < Task
+ attr_accessor :spec, :config
+
+ def initialize(gemspec, config={})
+ super(:package, "Build a gem package", "package", {})
+ @spec = gemspec
+ @config = {:dir => File.join(Dir.pwd, "pkg")}.merge(config)
+ end
+
+ def run(instance, args=[])
+ FileUtils.mkdir_p(config[:dir])
+ Gem::Builder.new(spec).build
+ FileUtils.mv(spec.file_name, File.join(config[:dir], spec.file_name))
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/spec.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/spec.rb
new file mode 100644
index 0000000000..c7d00968e8
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/spec.rb
@@ -0,0 +1,70 @@
+require "fileutils"
+
+class Thor
+ # Creates a spec task.
+ #
+ # ==== Parameters
+ # files<Array> - Array of files to spec
+ #
+ # ==== Options
+ # :name - The name of the task. It can be rcov or spec. Spec is the default.
+ # :rcov - A hash with rcov specific options.
+ # :rcov_dir - Where rcov reports should be printed.
+ # :verbose - Sets the default value for verbose, although it can be specified
+ # also through the command line.
+ #
+ # All other options are added to rspec.
+ #
+ def self.spec_task(files, options={})
+ name = (options.delete(:name) || 'spec').to_s
+ tasks[name] = Thor::SpecTask.new(name, files, options)
+ end
+
+ class SpecTask < Task
+ attr_accessor :name, :files, :rcov_dir, :rcov_config, :spec_config
+
+ def initialize(name, files, config={})
+ options = { :verbose => Thor::Option.parse(:verbose, config.delete(:verbose) || false) }
+ super(name, "#{name.capitalize} task", name, options)
+
+ @name = name
+ @files = files.map{ |f| %["#{f}"] }.join(" ")
+ @rcov_dir = config.delete(:rdoc_dir) || File.join(Dir.pwd, 'coverage')
+ @rcov_config = config.delete(:rcov) || {}
+ @spec_config = { :format => 'specdoc', :color => true }.merge(config)
+ end
+
+ def run(instance, args=[])
+ rcov_opts = Thor::Options.to_switches(rcov_config)
+ spec_opts = Thor::Options.to_switches(spec_config)
+
+ require 'rbconfig'
+ cmd = RbConfig::CONFIG['ruby_install_name'] << " "
+
+ if rcov?
+ FileUtils.rm_rf(rcov_dir)
+ cmd << "-S #{where('rcov')} -o #{rcov_dir} #{rcov_opts} "
+ end
+
+ cmd << [where('spec'), rcov? ? " -- " : nil, files, spec_opts].join(" ")
+
+ puts cmd if instance.options.verbose?
+ system(cmd)
+ exit($?.exitstatus)
+ end
+
+ private
+
+ def rcov?
+ name == "rcov"
+ end
+
+ def where(file)
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+ file_with_path = File.join(path, file)
+ next unless File.exist?(file_with_path) && File.executable?(file_with_path)
+ return File.expand_path(file_with_path)
+ end
+ end
+ end
+end
diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/util.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/util.rb
new file mode 100644
index 0000000000..26db24aadb
--- /dev/null
+++ b/railties/lib/vendor/thor-0.11.3/lib/thor/util.rb
@@ -0,0 +1,229 @@
+require 'rbconfig'
+
+class Thor
+ module Sandbox; end
+
+ # This module holds several utilities:
+ #
+ # 1) Methods to convert thor namespaces to constants and vice-versa.
+ #
+ # Thor::Utils.constant_to_namespace(Foo::Bar::Baz) #=> "foo:bar:baz"
+ # Thor::Utils.namespace_to_constant("foo:bar:baz") #=> Foo::Bar::Baz
+ #
+ # 2) Loading thor files and sandboxing:
+ #
+ # Thor::Utils.load_thorfile("~/.thor/foo")
+ #
+ module Util
+
+ # Receives a namespace and search for it in the Thor::Base subclasses.
+ #
+ # ==== Parameters
+ # namespace<String>:: The namespace to search for.
+ #
+ def self.find_by_namespace(namespace)
+ namespace = 'default' if namespace.empty?
+
+ Thor::Base.subclasses.find do |klass|
+ klass.namespace == namespace
+ end
+ end
+
+ # Receives a constant and converts it to a Thor namespace. Since Thor tasks
+ # can be added to a sandbox, this method is also responsable for removing
+ # the sandbox namespace.
+ #
+ # This method should not be used in general because it's used to deal with
+ # older versions of Thor. On current versions, if you need to get the
+ # namespace from a class, just call namespace on it.
+ #
+ # ==== Parameters
+ # constant<Object>:: The constant to be converted to the thor path.
+ #
+ # ==== Returns
+ # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
+ #
+ def self.constant_to_namespace(constant, remove_default=true)
+ constant = constant.to_s.gsub(/^Thor::Sandbox::/, "")
+ constant = snake_case(constant).squeeze(":")
+ constant.gsub!(/^default/, '') if remove_default
+ constant
+ end
+
+ # Given the contents, evaluate it inside the sandbox and returns the thor
+ # classes defined in the sandbox.
+ #
+ # ==== Parameters
+ # contents<String>
+ #
+ # ==== Returns
+ # Array[Object]
+ #
+ def self.namespaces_in_contents(contents, file=__FILE__)
+ old_constants = Thor::Base.subclasses.dup
+ Thor::Base.subclasses.clear
+
+ load_thorfile(file, contents)
+
+ new_constants = Thor::Base.subclasses.dup
+ Thor::Base.subclasses.replace(old_constants)
+
+ new_constants.map!{ |c| c.namespace }
+ new_constants.compact!
+ new_constants
+ end
+
+ # Receives a string and convert it to snake case. SnakeCase returns snake_case.
+ #
+ # ==== Parameters
+ # String
+ #
+ # ==== Returns
+ # String
+ #
+ def self.snake_case(str)
+ return str.downcase if str =~ /^[A-Z_]+$/
+ str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/
+ return $+.downcase
+ end
+
+ # Receives a namespace and tries to retrieve a Thor or Thor::Group class
+ # from it. It first searches for a class using the all the given namespace,
+ # if it's not found, removes the highest entry and searches for the class
+ # again. If found, returns the highest entry as the class name.
+ #
+ # ==== Examples
+ #
+ # class Foo::Bar < Thor
+ # def baz
+ # end
+ # end
+ #
+ # class Baz::Foo < Thor::Group
+ # end
+ #
+ # Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default task
+ # Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil
+ # Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz"
+ #
+ # ==== Parameters
+ # namespace<String>
+ #
+ # ==== Errors
+ # Thor::Error:: raised if the namespace cannot be found.
+ #
+ # Thor::Error:: raised if the namespace evals to a class which does not
+ # inherit from Thor or Thor::Group.
+ #
+ def self.namespace_to_thor_class(namespace, raise_if_nil=true)
+ klass, task_name = Thor::Util.find_by_namespace(namespace), nil
+
+ if klass.nil? && namespace.include?(?:)
+ namespace = namespace.split(":")
+ task_name = namespace.pop
+ klass = Thor::Util.find_by_namespace(namespace.join(":"))
+ end
+
+ raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil?
+
+ return klass, task_name
+ end
+
+ # Receives a path and load the thor file in the path. The file is evaluated
+ # inside the sandbox to avoid namespacing conflicts.
+ #
+ def self.load_thorfile(path, content=nil)
+ content ||= File.read(path)
+
+ begin
+ Thor::Sandbox.class_eval(content, path)
+ rescue Exception => e
+ $stderr.puts "WARNING: unable to load thorfile #{path.inspect}: #{e.message}"
+ end
+ end
+
+ # Receives a yaml (hash) and updates all constants entries to namespace.
+ # This was added to deal with deprecated versions of Thor.
+ #
+ # TODO Deprecate this method in the future.
+ #
+ # ==== Returns
+ # TrueClass|FalseClass:: Returns true if any change to the yaml file was made.
+ #
+ def self.convert_constants_to_namespaces(yaml)
+ yaml_changed = false
+
+ yaml.each do |k, v|
+ next unless v[:constants] && v[:namespaces].nil?
+ yaml_changed = true
+ yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.constant_to_namespace(c)}
+ end
+
+ yaml_changed
+ end
+
+ def self.user_home
+ @@user_home ||= if ENV["HOME"]
+ ENV["HOME"]
+ elsif ENV["USERPROFILE"]
+ ENV["USERPROFILE"]
+ elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"]
+ File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"])
+ elsif ENV["APPDATA"]
+ ENV["APPDATA"]
+ else
+ begin
+ File.expand_path("~")
+ rescue
+ if File::ALT_SEPARATOR
+ "C:/"
+ else
+ "/"
+ end
+ end
+ end
+ end
+
+ # Returns the root where thor files are located, dependending on the OS.
+ #
+ def self.thor_root
+ File.join(user_home, ".thor")
+ end
+
+ # Returns the files in the thor root. On Windows thor_root will be something
+ # like this:
+ #
+ # C:\Documents and Settings\james\.thor
+ #
+ # If we don't #gsub the \ character, Dir.glob will fail.
+ #
+ def self.thor_root_glob
+ files = Dir["#{thor_root.gsub(/\\/, '/')}/*"]
+
+ files.map! do |file|
+ File.directory?(file) ? File.join(file, "main.thor") : file
+ end
+ end
+
+ # Where to look for Thor files.
+ #
+ def self.globs_for(path)
+ ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"]
+ end
+
+ # Return the path to the ruby interpreter taking into account multiple
+ # installations and windows extensions.
+ #
+ def self.ruby_command #:nodoc:
+ @ruby_command ||= begin
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+ ruby << Config::CONFIG['EXEEXT']
+
+ # escape string in case path to ruby executable contain spaces.
+ ruby.sub!(/.*\s.*/m, '"\&"')
+ ruby
+ end
+ end
+
+ end
+end
diff --git a/railties/test/boot_test.rb b/railties/test/boot_test.rb
index 2dd68857c3..7bf420d4f2 100644
--- a/railties/test/boot_test.rb
+++ b/railties/test/boot_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
require 'initializer'
-require "#{File.dirname(__FILE__)}/../environments/boot"
+require "#{File.dirname(__FILE__)}/../lib/generators/rails/app/templates/config/boot"
require 'rails/gem_dependency'
class BootTest < Test::Unit::TestCase
diff --git a/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb b/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb
new file mode 100644
index 0000000000..7a4edb8bcb
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb
@@ -0,0 +1,8 @@
+require 'generators/active_record'
+
+module ActiveRecord
+ module Generators
+ class FixjourGenerator < Base
+ end
+ end
+end
diff --git a/railties/test/fixtures/lib/generators/fixjour_generator.rb b/railties/test/fixtures/lib/generators/fixjour_generator.rb
new file mode 100644
index 0000000000..ef3e9edbed
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/fixjour_generator.rb
@@ -0,0 +1,2 @@
+class FixjourGenerator < Rails::Generators::NamedBase
+end
diff --git a/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb b/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb
new file mode 100644
index 0000000000..cad5e96784
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb
@@ -0,0 +1,4 @@
+module Rails::Generators
+ class JavascriptsGenerator < Rails::Generators::NamedBase
+ end
+end
diff --git a/railties/test/fixtures/lib/generators/working/working_generator.rb b/railties/test/fixtures/lib/generators/working/working_generator.rb
deleted file mode 100644
index 465b34319a..0000000000
--- a/railties/test/fixtures/lib/generators/working/working_generator.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class WorkingGenerator < Rails::Generator::NamedBase
-end
diff --git a/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb b/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb
index 2d373ce422..4ca4ddd447 100644
--- a/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb
+++ b/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb
@@ -1,5 +1,5 @@
-class MetalA < Rails::Rack::Metal
+class MetalA
def self.call(env)
- [200, { "Content-Type" => "text/html"}, ["Hi"]]
+ [404, { "Content-Type" => "text/html"}, ["Metal A"]]
end
end
diff --git a/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb b/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb
index a8bbf3fd60..80e69fe0b0 100644
--- a/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb
+++ b/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb
@@ -1,5 +1,5 @@
-class MetalB < Rails::Rack::Metal
+class MetalB
def self.call(env)
- [200, { "Content-Type" => "text/html"}, ["Hi"]]
+ [200, { "Content-Type" => "text/html"}, ["Metal B"]]
end
end
diff --git a/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb b/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb
deleted file mode 100644
index 8fda8197d1..0000000000
--- a/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-class StubbyGenerator < Rails::Generator::Base
- def manifest
- end
-end
diff --git a/railties/test/fixtures/vendor/gems/mspec/lib/generators/mspec_generator.rb b/railties/test/fixtures/vendor/gems/mspec/lib/generators/mspec_generator.rb
new file mode 100644
index 0000000000..191bdbf2fc
--- /dev/null
+++ b/railties/test/fixtures/vendor/gems/mspec/lib/generators/mspec_generator.rb
@@ -0,0 +1,2 @@
+class MspecGenerator < Rails::Generators::NamedBase
+end
diff --git a/railties/test/fixtures/vendor/gems/wrong/lib/generators/wrong_generator.rb b/railties/test/fixtures/vendor/gems/wrong/lib/generators/wrong_generator.rb
new file mode 100644
index 0000000000..6aa7cb052e
--- /dev/null
+++ b/railties/test/fixtures/vendor/gems/wrong/lib/generators/wrong_generator.rb
@@ -0,0 +1,3 @@
+# Old generator version
+class WrongGenerator < Rails::Generator::NamedBase
+end
diff --git a/railties/test/generator_lookup_test.rb b/railties/test/generator_lookup_test.rb
deleted file mode 100644
index b67087e5ea..0000000000
--- a/railties/test/generator_lookup_test.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'plugin_test_helper'
-
-class GeneratorLookupTest < Test::Unit::TestCase
- def setup
- @fixture_dirs = %w{alternate default}
- @configuration = Rails.configuration = Rails::Configuration.new
- # We need to add our testing plugin directory to the plugin paths so
- # the locator knows where to look for our plugins
- @configuration.plugin_paths += @fixture_dirs.map{|fd| plugin_fixture_path(fd)}
- @initializer = Rails::Initializer.default
- @initializer.config = @configuration
- @initializer.run(:add_plugin_load_paths)
- @initializer.run(:load_plugins)
- @initializer.run(:set_root_path)
- load 'rails_generator.rb'
- require 'rails_generator/scripts'
- end
-
- def test_should_load_from_all_plugin_paths
- assert Rails::Generator::Base.lookup('a_generator')
- assert Rails::Generator::Base.lookup('stubby_generator')
- end
-
- def test_should_create_generator_source_for_each_directory_in_plugin_paths
- sources = Rails::Generator::Base.sources
- @fixture_dirs.each do |gen_dir|
- expected_label = "plugins (fixtures/plugins/#{gen_dir})".to_sym
- assert sources.any? {|source| source.label == expected_label }
- end
- end
-
- def test_should_preserve_order_in_usage_message
- msg = Rails::Generator::Scripts::Base.new.send(:usage_message)
- positions = @fixture_dirs.map do |gen_dir|
- pos = msg.index("Plugins (fixtures/plugins/#{gen_dir})")
- assert_not_nil pos
- pos
- end
- assert_equal positions.sort, positions
- end
-
-end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
new file mode 100644
index 0000000000..0cda49702b
--- /dev/null
+++ b/railties/test/generators/actions_test.rb
@@ -0,0 +1,177 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/app/app_generator'
+
+class ActionsTest < GeneratorsTestCase
+ def setup
+ super
+ @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git'
+ @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
+ 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'
+ end
+
+ def test_create_file_should_write_block_contents_to_file_path
+ action(:create_file, 'lib/test_file.rb'){ 'heres block data' }
+ assert_file 'lib/test_file.rb', 'heres block data'
+ end
+
+ def test_plugin_with_git_option_should_run_plugin_install
+ generator.expects(:run_ruby_script).once.with("script/plugin install #{@git_plugin_uri}", :verbose => false)
+ action :plugin, 'restful-authentication', :git => @git_plugin_uri
+ end
+
+ def test_plugin_with_svn_option_should_run_plugin_install
+ generator.expects(:run_ruby_script).once.with("script/plugin install #{@svn_plugin_uri}", :verbose => false)
+ action :plugin, 'restful-authentication', :svn => @svn_plugin_uri
+ end
+
+ def test_plugin_with_git_option_and_submodule_should_use_git_scm
+ generator.expects(:run).with("git submodule add #{@git_plugin_uri} vendor/plugins/rest_auth", :verbose => false)
+ action :plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true
+ end
+
+ def test_plugin_with_no_options_should_skip_method
+ generator.expects(:run).never
+ action :plugin, 'rest_auth', {}
+ end
+
+ def test_gem_should_put_gem_dependency_in_enviroment
+ run_generator
+ action :gem, 'will-paginate'
+ assert_file 'config/environment.rb', /config\.gem 'will\-paginate'/
+ end
+
+ def test_gem_with_options_should_include_options_in_gem_dependency_in_environment
+ run_generator
+ action :gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'
+
+ regexp = /#{Regexp.escape("config.gem 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'")}/
+ assert_file 'config/environment.rb', regexp
+ end
+
+ def test_gem_with_env_string_should_put_gem_dependency_in_specified_environment
+ run_generator
+ action :gem, 'rspec', :env => 'test'
+ assert_file 'config/environments/test.rb', /config\.gem 'rspec'/
+ end
+
+ def test_gem_with_env_array_should_put_gem_dependency_in_specified_environments
+ run_generator
+ action :gem, 'quietbacktrace', :env => %w[ development test ]
+ assert_file 'config/environments/development.rb', /config\.gem 'quietbacktrace'/
+ assert_file 'config/environments/test.rb', /config\.gem 'quietbacktrace'/
+ end
+
+ def test_gem_with_lib_option_set_to_false_should_put_gem_dependency_in_enviroment_correctly
+ run_generator
+ action :gem, 'mislav-will-paginate', :lib => false
+ assert_file 'config/environment.rb', /config\.gem 'mislav\-will\-paginate'\, :lib => false/
+ end
+
+ def test_environment_should_include_data_in_environment_initializer_block
+ run_generator
+ load_paths = 'config.load_paths += %w["#{RAILS_ROOT}/app/extras"]'
+ action :environment, load_paths
+ assert_file 'config/environment.rb', /#{Regexp.escape(load_paths)}/
+ end
+
+ def test_environment_with_block_should_include_block_contents_in_environment_initializer_block
+ run_generator
+
+ action :environment do
+ '# This wont be added'
+ '# This will be added'
+ end
+
+ assert_file 'config/environment.rb' do |content|
+ assert_match /# This will be added/, content
+ assert_no_match /# This wont be added/, content
+ end
+ end
+
+ def test_git_with_symbol_should_run_command_using_git_scm
+ generator.expects(:run).once.with('git init')
+ action :git, :init
+ end
+
+ def test_git_with_hash_should_run_each_command_using_git_scm
+ generator.expects(:run).times(2)
+ action :git, :rm => 'README', :add => '.'
+ end
+
+ def test_vendor_should_write_data_to_file_in_vendor
+ action :vendor, 'vendor_file.rb', '# vendor data'
+ assert_file 'vendor/vendor_file.rb', '# vendor data'
+ end
+
+ def test_lib_should_write_data_to_file_in_lib
+ action :lib, 'my_library.rb', 'class MyLibrary'
+ assert_file 'lib/my_library.rb', 'class MyLibrary'
+ end
+
+ def test_rakefile_should_write_date_to_file_in_lib_tasks
+ action :rakefile, 'myapp.rake', 'task :run => [:environment]'
+ assert_file 'lib/tasks/myapp.rake', 'task :run => [:environment]'
+ end
+
+ def test_initializer_should_write_date_to_file_in_config_initializers
+ action :initializer, 'constants.rb', 'MY_CONSTANT = 42'
+ assert_file 'config/initializers/constants.rb', 'MY_CONSTANT = 42'
+ end
+
+ def test_generate_should_run_script_generate_with_argument_and_options
+ generator.expects(:run_ruby_script).once.with('script/generate model MyModel', :verbose => false)
+ action :generate, 'model', 'MyModel'
+ end
+
+ def test_rake_should_run_rake_command_with_development_env
+ generator.expects(:run).once.with('rake log:clear RAILS_ENV=development', :verbose => false)
+ action :rake, 'log:clear'
+ end
+
+ def test_rake_with_env_option_should_run_rake_command_in_env
+ generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', :verbose => false)
+ action :rake, 'log:clear', :env => 'production'
+ end
+
+ def test_rake_with_sudo_option_should_run_rake_command_with_sudo
+ generator.expects(:run).once.with('sudo rake log:clear RAILS_ENV=development', :verbose => false)
+ action :rake, 'log:clear', :sudo => true
+ end
+
+ def test_capify_should_run_the_capify_command
+ generator.expects(:run).once.with('capify .', :verbose => false)
+ action :capify!
+ end
+
+ def test_freeze_should_freeze_rails_edge
+ generator.expects(:run).once.with('rake rails:freeze:edge', :verbose => false)
+ action :freeze!
+ end
+
+ def test_route_should_add_data_to_the_routes_block_in_config_routes
+ run_generator
+ route_command = "map.route '/login', :controller => 'sessions', :action => 'new'"
+ action :route, route_command
+ assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/
+ end
+
+ protected
+
+ def run_generator
+ silence(:stdout) { Rails::Generators::AppGenerator.start [destination_root] }
+ end
+
+ def generator(config={})
+ @generator ||= Rails::Generators::Base.new([], {}, { :destination_root => destination_root }.merge!(config))
+ end
+
+ def action(*args, &block)
+ silence(:stdout){ generator.send(*args, &block) }
+ end
+
+end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
new file mode 100644
index 0000000000..c794a2ade6
--- /dev/null
+++ b/railties/test/generators/app_generator_test.rb
@@ -0,0 +1,182 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/app/app_generator'
+
+class AppGeneratorTest < GeneratorsTestCase
+
+ def setup
+ super
+ Rails::Generators::AppGenerator.instance_variable_set('@desc', nil)
+ end
+
+ def teardown
+ super
+ Rails::Generators::AppGenerator.instance_variable_set('@desc', nil)
+ end
+
+ def test_application_skeleton_is_created
+ run_generator
+
+ %w(
+ app/controllers
+ app/helpers
+ app/models
+ app/views/layouts
+ config/environments
+ config/initializers
+ config/locales
+ db
+ doc
+ lib
+ lib/tasks
+ log
+ public/images
+ public/javascripts
+ public/stylesheets
+ script/performance
+ test/fixtures
+ test/functional
+ test/integration
+ test/performance
+ test/unit
+ vendor
+ vendor/plugins
+ tmp/sessions
+ tmp/sockets
+ tmp/cache
+ tmp/pids
+ ).each{ |path| assert_file path }
+ end
+
+ def test_invalid_database_option_raises_an_error
+ content = capture(:stderr){ run_generator(["-d", "unknown"]) }
+ assert_match /Invalid value for \-\-database option/, content
+ end
+
+ def test_dispatchers_are_not_added_by_default
+ run_generator
+ assert_no_file "config.ru"
+ assert_no_file "public/dispatch.cgi"
+ assert_no_file "public/dispatch.fcgi"
+ end
+
+ def test_dispatchers_are_added_if_required
+ run_generator ["--with-dispatchers"]
+ assert_file "config.ru"
+ assert_file "public/dispatch.cgi"
+ assert_file "public/dispatch.fcgi"
+ end
+
+ def test_config_database_is_added_by_default
+ run_generator
+ assert_file "config/database.yml", /sqlite3/
+ end
+
+ def test_config_database_is_not_added_if_skip_activerecord_is_given
+ run_generator ["--skip-activerecord"]
+ assert_no_file "config/database.yml"
+ end
+
+ def test_activerecord_is_removed_from_frameworks_if_skip_activerecord_is_given
+ run_generator ["--skip-activerecord"]
+ assert_file "config/environment.rb", /config\.frameworks \-= \[ :active_record \]/
+ end
+
+ def test_prototype_and_test_unit_are_added_by_default
+ run_generator
+ assert_file "public/javascripts/prototype.js"
+ assert_file "test"
+ end
+
+ def test_prototype_and_test_unit_are_skipped_if_required
+ run_generator ["--skip-prototype", "--skip-testunit"]
+ assert_no_file "public/javascripts/prototype.js"
+ assert_no_file "test"
+ end
+
+ def test_shebang_is_added_to_files
+ run_generator ["--ruby", "foo/bar/baz"]
+
+ %w(
+ about
+ console
+ dbconsole
+ destroy
+ generate
+ plugin
+ runner
+ server
+ ).each { |path| assert_file "script/#{path}", /#!foo\/bar\/baz/ }
+ end
+
+ def test_shebang_when_is_the_same_as_default_use_env
+ run_generator ["--ruby", Thor::Util.ruby_command]
+
+ %w(
+ about
+ console
+ dbconsole
+ destroy
+ generate
+ plugin
+ runner
+ server
+ ).each { |path| assert_file "script/#{path}", /#!\/usr\/bin\/env/ }
+ end
+
+ def test_rails_is_frozen
+ generator(:freeze => true, :database => "sqlite3").expects(:run).
+ with("rake rails:freeze:edge", :verbose => false)
+ silence(:stdout){ generator.invoke }
+ assert_file 'config/environment.rb', /# RAILS_GEM_VERSION/
+ end
+
+ def test_template_raises_an_error_with_invalid_path
+ content = capture(:stderr){ run_generator(["-m", "non/existant/path"]) }
+ assert_match /The template \[.*\] could not be loaded/, content
+ assert_match /non\/existant\/path/, content
+ end
+
+ def test_template_is_executed_when_supplied
+ path = "http://gist.github.com/103208.txt"
+ template = %{ say "It works!" }
+ template.instance_eval "def read; self; end" # Make the string respond to read
+
+ generator(:template => path, :database => "sqlite3").expects(:open).with(path).returns(template)
+ assert_match /It works!/, silence(:stdout){ generator.invoke }
+ end
+
+ def test_usage_read_from_file
+ File.expects(:read).returns("USAGE FROM FILE")
+ assert_equal "USAGE FROM FILE", Rails::Generators::AppGenerator.desc
+ end
+
+ def test_default_usage
+ File.expects(:exist?).returns(false)
+ assert_match /Create rails files for app generator/, Rails::Generators::AppGenerator.desc
+ end
+
+ def test_default_namespace
+ assert_match "rails:generators:app", Rails::Generators::AppGenerator.namespace
+ end
+
+ def test_file_is_added_for_backwards_compatibility
+ action :file, 'lib/test_file.rb', 'heres test data'
+ assert_file 'lib/test_file.rb', 'heres test data'
+ end
+
+ protected
+
+ def run_generator(args=[])
+ silence(:stdout) { Rails::Generators::AppGenerator.start [destination_root].concat(args) }
+ end
+
+ def generator(options={})
+ @generator ||= Rails::Generators::AppGenerator.new([destination_root], options, :destination_root => destination_root)
+ end
+
+ def action(*args, &block)
+ silence(:stdout){ generator.send(*args, &block) }
+ end
+
+end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
new file mode 100644
index 0000000000..850b9e632d
--- /dev/null
+++ b/railties/test/generators/controller_generator_test.rb
@@ -0,0 +1,80 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/controller/controller_generator'
+
+class ControllerGeneratorTest < GeneratorsTestCase
+
+ def test_help_does_not_show_invoked_generators_options_if_they_already_exist
+ content = run_generator ["--help"]
+ assert_no_match /Helper options:/, content
+ end
+
+ def test_controller_skeleton_is_created
+ run_generator
+ assert_file "app/controllers/account_controller.rb", /class AccountController < ApplicationController/
+ end
+
+ def test_check_class_collision
+ Object.send :const_set, :ObjectController, Class.new
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match /The name 'ObjectController' is either already used in your application or reserved/, content
+ ensure
+ Object.send :remove_const, :ObjectController
+ end
+
+ # No need to spec content since it's already spec'ed on helper generator.
+ #
+ def test_invokes_helper
+ run_generator
+ assert_file "app/helpers/account_helper.rb"
+ assert_file "test/unit/helpers/account_helper_test.rb"
+ end
+
+ def test_does_not_invoke_helper_if_required
+ run_generator ["account", "--skip-helper"]
+ assert_no_file "app/helpers/account_helper.rb"
+ assert_no_file "test/unit/helpers/account_helper_test.rb"
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/functional/account_controller_test.rb"
+ end
+
+ def test_does_not_invoke_test_framework_if_required
+ run_generator ["account", "--no-test-framework"]
+ assert_no_file "test/functional/account_controller_test.rb"
+ end
+
+ def test_invokes_default_template_engine
+ run_generator
+ assert_file "app/views/account/foo.html.erb", /app\/views\/account\/foo/
+ assert_file "app/views/account/bar.html.erb", /app\/views\/account\/bar/
+ end
+
+ def test_invokes_default_template_engine_even_with_no_action
+ run_generator ["account"]
+ assert_file "app/views/account"
+ end
+
+ def test_template_engine_with_class_path
+ run_generator ["admin/account"]
+ assert_file "app/views/admin/account"
+ end
+
+ def test_actions_are_turned_into_methods
+ run_generator
+
+ assert_file "app/controllers/account_controller.rb" do |controller|
+ assert_instance_method controller, :foo
+ assert_instance_method controller, :bar
+ end
+ end
+
+ protected
+
+ def run_generator(args=["Account", "foo", "bar"])
+ silence(:stdout) { Rails::Generators::ControllerGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb
new file mode 100644
index 0000000000..b1ebe5f1b5
--- /dev/null
+++ b/railties/test/generators/generator_generator_test.rb
@@ -0,0 +1,26 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/generator/generator_generator'
+
+class GeneratorGeneratorTest < GeneratorsTestCase
+
+ def test_generator_skeleton_is_created
+ run_generator
+
+ %w(
+ lib/generators/awesome
+ lib/generators/awesome/USAGE
+ lib/generators/awesome/templates
+ ).each{ |path| assert_file path }
+
+ assert_file "lib/generators/awesome/awesome_generator.rb",
+ /class AwesomeGenerator < Rails::Generators::NamedBase/
+ end
+
+ protected
+
+ def run_generator(args=["awesome"], config={})
+ silence(:stdout) { Rails::Generators::GeneratorGenerator.start args, config.merge(:destination_root => destination_root) }
+ end
+
+end
diff --git a/railties/test/generators/generator_test_helper.rb b/railties/test/generators/generator_test_helper.rb
deleted file mode 100644
index 01bf1c90bd..0000000000
--- a/railties/test/generators/generator_test_helper.rb
+++ /dev/null
@@ -1,303 +0,0 @@
-require 'test/unit'
-require 'fileutils'
-
-# Mock out what we need from AR::Base
-module ActiveRecord
- class Base
- class << self
- attr_accessor :pluralize_table_names, :timestamped_migrations
- end
- self.pluralize_table_names = true
- self.timestamped_migrations = true
- end
-
- module ConnectionAdapters
- class Column
- attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
-
- def initialize(name, default, sql_type = nil)
- @name = name
- @default = default
- @type = @sql_type = sql_type
- end
-
- def human_name
- @name.humanize
- end
- end
- end
-end
-
-# Mock up necessities from ActionView
-module ActionView
- module Helpers
- module ActionRecordHelper; end
- class InstanceTag; end
- end
-end
-
-# Set RAILS_ROOT appropriately fixture generation
-tmp_dir = "#{File.dirname(__FILE__)}/../fixtures/tmp"
-
-if defined? RAILS_ROOT
- RAILS_ROOT.replace tmp_dir
-else
- RAILS_ROOT = tmp_dir
-end
-FileUtils.mkdir_p RAILS_ROOT
-
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"
-require 'initializer'
-
-# Mocks out the configuration
-module Rails
- def self.configuration
- Rails::Configuration.new
- end
-end
-
-require 'rails_generator'
-
-class GeneratorTestCase < Test::Unit::TestCase
- include FileUtils
-
- def setup
- ActiveRecord::Base.pluralize_table_names = true
-
- mkdir_p "#{RAILS_ROOT}/app/views/layouts"
- mkdir_p "#{RAILS_ROOT}/config"
- mkdir_p "#{RAILS_ROOT}/db"
- mkdir_p "#{RAILS_ROOT}/test/fixtures"
- mkdir_p "#{RAILS_ROOT}/public/stylesheets"
-
- File.open("#{RAILS_ROOT}/config/routes.rb", 'w') do |f|
- f << "ActionController::Routing::Routes.draw do |map|\n\nend"
- end
- end
-
- def teardown
- rm_rf "#{RAILS_ROOT}/app"
- rm_rf "#{RAILS_ROOT}/test"
- rm_rf "#{RAILS_ROOT}/config"
- rm_rf "#{RAILS_ROOT}/db"
- rm_rf "#{RAILS_ROOT}/public"
- end
-
- def test_truth
- # don't complain, test/unit
- end
-
- # Instantiates the Generator.
- def build_generator(name, params)
- Rails::Generator::Base.instance(name, params)
- end
-
- # Runs the +create+ command (like the command line does).
- def run_generator(name, params)
- silence_generator do
- build_generator(name, params).command(:create).invoke!
- end
- end
-
- # Silences the logger temporarily and returns the output as a String.
- def silence_generator
- logger_original = Rails::Generator::Base.logger
- myout = StringIO.new
- Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(myout)
- yield if block_given?
- Rails::Generator::Base.logger = logger_original
- myout.string
- end
-
- # Asserts that the given controller was generated.
- # It takes a name or symbol without the <tt>_controller</tt> part and an optional super class.
- # The contents of the class source file is passed to a block.
- def assert_generated_controller_for(name, parent = "ApplicationController")
- assert_generated_class "app/controllers/#{name.to_s.underscore}_controller", parent do |body|
- yield body if block_given?
- end
- end
-
- # Asserts that the given model was generated.
- # It takes a name or symbol and an optional super class.
- # The contents of the class source file is passed to a block.
- def assert_generated_model_for(name, parent = "ActiveRecord::Base")
- assert_generated_class "app/models/#{name.to_s.underscore}", parent do |body|
- yield body if block_given?
- end
- end
-
- # Asserts that the given helper was generated.
- # It takes a name or symbol without the <tt>_helper</tt> part.
- # The contents of the module source file is passed to a block.
- def assert_generated_helper_for(name)
- assert_generated_module "app/helpers/#{name.to_s.underscore}_helper" do |body|
- yield body if block_given?
- end
- end
-
- # Asserts that the given functional test was generated.
- # It takes a name or symbol without the <tt>_controller_test</tt> part and an optional super class.
- # The contents of the class source file is passed to a block.
- def assert_generated_functional_test_for(name, parent = "ActionController::TestCase")
- assert_generated_class "test/functional/#{name.to_s.underscore}_controller_test",parent do |body|
- yield body if block_given?
- end
- end
-
- # Asserts that the given helper test test was generated.
- # It takes a name or symbol without the <tt>_helper_test</tt> part and an optional super class.
- # The contents of the class source file is passed to a block.
- def assert_generated_helper_test_for(name, parent = "ActionView::TestCase")
- path = "test/unit/helpers/#{name.to_s.underscore}_helper_test"
- # Have to pass the path without the "test/" part so that class_name_from_path will return a correct result
- class_name = class_name_from_path(path.gsub(/^test\//, ''))
-
- assert_generated_class path,parent,class_name do |body|
- yield body if block_given?
- end
- end
-
- # Asserts that the given unit test was generated.
- # It takes a name or symbol without the <tt>_test</tt> part and an optional super class.
- # The contents of the class source file is passed to a block.
- def assert_generated_unit_test_for(name, parent = "ActiveSupport::TestCase")
- assert_generated_class "test/unit/#{name.to_s.underscore}_test", parent do |body|
- yield body if block_given?
- end
- end
-
- # Asserts that the given file was generated.
- # The contents of the file is passed to a block.
- def assert_generated_file(path)
- assert_file_exists(path)
- File.open("#{RAILS_ROOT}/#{path}") do |f|
- yield f.read if block_given?
- end
- end
-
- # asserts that the given file exists
- def assert_file_exists(path)
- assert File.exist?("#{RAILS_ROOT}/#{path}"),
- "The file '#{RAILS_ROOT}/#{path}' should exist"
- end
-
- # Asserts that the given class source file was generated.
- # It takes a path without the <tt>.rb</tt> part and an optional super class.
- # The contents of the class source file is passed to a block.
- def assert_generated_class(path, parent = nil, class_name = class_name_from_path(path))
- assert_generated_file("#{path}.rb") do |body|
- assert_match /class #{class_name}#{parent.nil? ? '':" < #{parent}"}/, body, "the file '#{path}.rb' should be a class"
- yield body if block_given?
- end
- end
-
- def class_name_from_path(path)
- # FIXME: Sucky way to detect namespaced classes
- if path.split('/').size > 3
- path =~ /\/?(\d+_)?(\w+)\/(\w+)$/
- "#{$2.camelize}::#{$3.camelize}"
- else
- path =~ /\/?(\d+_)?(\w+)$/
- $2.camelize
- end
- end
-
- # Asserts that the given module source file was generated.
- # It takes a path without the <tt>.rb</tt> part.
- # The contents of the class source file is passed to a block.
- def assert_generated_module(path)
- # FIXME: Sucky way to detect namespaced modules
- if path.split('/').size > 3
- path =~ /\/?(\w+)\/(\w+)$/
- module_name = "#{$1.camelize}::#{$2.camelize}"
- else
- path =~ /\/?(\w+)$/
- module_name = $1.camelize
- end
-
- assert_generated_file("#{path}.rb") do |body|
- assert_match /module #{module_name}/, body, "the file '#{path}.rb' should be a module"
- yield body if block_given?
- end
- end
-
- # Asserts that the given CSS stylesheet file was generated.
- # It takes a path without the <tt>.css</tt> part.
- # The contents of the stylesheet source file is passed to a block.
- def assert_generated_stylesheet(path)
- assert_generated_file("public/stylesheets/#{path}.css") do |body|
- yield body if block_given?
- end
- end
-
- # Asserts that the given YAML file was generated.
- # It takes a path without the <tt>.yml</tt> part.
- # The parsed YAML tree is passed to a block.
- def assert_generated_yaml(path)
- assert_generated_file("#{path}.yml") do |body|
- yaml = YAML.load(body)
- assert yaml, 'YAML data missing'
- yield yaml if block_given?
- end
- end
-
- # Asserts that the given fixtures YAML file was generated.
- # It takes a fixture name without the <tt>.yml</tt> part.
- # The parsed YAML tree is passed to a block.
- def assert_generated_fixtures_for(name)
- assert_generated_yaml "test/fixtures/#{name.to_s.underscore}" do |yaml|
- yield yaml if block_given?
- end
- end
-
- # Asserts that the given views were generated.
- # It takes a controller name and a list of views (including extensions).
- # The body of each view is passed to a block.
- def assert_generated_views_for(name, *actions)
- actions.each do |action|
- assert_generated_file("app/views/#{name.to_s.underscore}/#{action}") do |body|
- yield body if block_given?
- end
- end
- end
-
- def assert_generated_migration(name, parent = "ActiveRecord::Migration")
- file = Dir.glob("#{RAILS_ROOT}/db/migrate/*_#{name.to_s.underscore}.rb").first
- file = file.match(/db\/migrate\/[0-9]+_\w+/).to_s
- assert_generated_class file, parent do |body|
- assert_match /timestamps/, body, "should have timestamps defined"
- yield body if block_given?
- end
- end
-
- # Asserts that the given migration file was not generated.
- # It takes the name of the migration as a parameter.
- def assert_skipped_migration(name)
- migration_file = "#{RAILS_ROOT}/db/migrate/001_#{name.to_s.underscore}.rb"
- assert !File.exist?(migration_file), "should not create migration #{migration_file}"
- end
-
- # Asserts that the given resource was added to the routes.
- def assert_added_route_for(name)
- assert_generated_file("config/routes.rb") do |body|
- assert_match /map.resources :#{name.to_s.underscore}/, body,
- "should add route for :#{name.to_s.underscore}"
- end
- end
-
- # Asserts that the given methods are defined in the body.
- # This does assume standard rails code conventions with regards to the source code.
- # The body of each individual method is passed to a block.
- def assert_has_method(body, *methods)
- methods.each do |name|
- assert body =~ /^ def #{name}(\(.+\))?\n((\n| .*\n)*) end/, "should have method #{name}"
- yield(name, $2) if block_given?
- end
- end
-
- # Asserts that the given column is defined in the migration.
- def assert_generated_column(body, name, type)
- assert_match /t\.#{type.to_s} :#{name.to_s}/, body, "should have column #{name.to_s} defined"
- end
-end
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
new file mode 100644
index 0000000000..011bd518f8
--- /dev/null
+++ b/railties/test/generators/generators_test_helper.rb
@@ -0,0 +1,101 @@
+require 'test/unit'
+require 'fileutils'
+
+fixtures = File.expand_path(File.join(File.dirname(__FILE__), '..', 'fixtures'))
+if defined?(RAILS_ROOT)
+ RAILS_ROOT.replace fixtures
+else
+ RAILS_ROOT = fixtures
+end
+
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activerecord/lib"
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"
+require 'generators'
+
+CURRENT_PATH = File.expand_path(Dir.pwd)
+Rails::Generators.no_color!
+
+class GeneratorsTestCase < Test::Unit::TestCase
+ include FileUtils
+
+ def destination_root
+ @destination_root ||= File.expand_path(File.join(File.dirname(__FILE__),
+ '..', '..', 'fixtures', 'tmp'))
+ end
+
+ def setup
+ cd CURRENT_PATH
+ rm_rf(destination_root)
+ mkdir_p(destination_root)
+ end
+
+ def test_truth
+ # don't complain, test/unit
+ end
+
+ def capture(stream)
+ begin
+ stream = stream.to_s
+ eval "$#{stream} = StringIO.new"
+ yield
+ result = eval("$#{stream}").string
+ ensure
+ eval("$#{stream} = #{stream.upcase}")
+ end
+
+ result
+ end
+ alias :silence :capture
+
+ def assert_file(relative, *contents)
+ absolute = File.join(destination_root, relative)
+ assert File.exists?(absolute), "Expected file #{relative.inspect} to exist, but does not"
+
+ read = File.read(absolute) if block_given? || !contents.empty?
+ yield read if block_given?
+
+ contents.each do |content|
+ case content
+ when String
+ assert_equal content, read
+ when Regexp
+ assert_match content, read
+ end
+ end
+ end
+
+ def assert_no_file(relative)
+ absolute = File.join(destination_root, relative)
+ assert !File.exists?(absolute), "Expected file #{relative.inspect} to not exist, but does"
+ end
+
+ def assert_migration(relative, *contents, &block)
+ file_name = migration_file_name(relative)
+ assert file_name, "Expected migration #{relative} to exist, but was not found"
+ assert_file File.join(File.dirname(relative), file_name), *contents, &block
+ end
+
+ def assert_no_migration(relative)
+ file_name = migration_file_name(relative)
+ assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}"
+ end
+
+ def assert_class_method(content, method, &block)
+ assert_instance_method content, "self.#{method}", &block
+ end
+
+ def assert_instance_method(content, method)
+ assert content =~ /def #{method}(\(.+\))?(.*?)\n end/m, "Expected to have method #{method}"
+ yield $2.strip if block_given?
+ end
+
+ protected
+
+ def migration_file_name(relative)
+ absolute = File.join(destination_root, relative)
+ dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '')
+
+ migration = Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
+ File.basename(migration) if migration
+ end
+end
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
new file mode 100644
index 0000000000..3d655016f1
--- /dev/null
+++ b/railties/test/generators/helper_generator_test.rb
@@ -0,0 +1,60 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/helper/helper_generator'
+
+ObjectHelper = Class.new
+AnotherObjectHelperTest = Class.new
+
+class HelperGeneratorTest < GeneratorsTestCase
+
+ def test_helper_skeleton_is_created
+ run_generator
+ assert_file "app/helpers/admin_helper.rb", /module AdminHelper/
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/unit/helpers/admin_helper_test.rb", /class AdminHelperTest < ActionView::TestCase/
+ end
+
+ def test_logs_if_the_test_framework_cannot_be_found
+ content = run_generator ["admin", "--test-framework=rspec"]
+ assert_match /rspec \[not found\]/, content
+ end
+
+ def test_check_class_collision
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match /The name 'ObjectHelper' is either already used in your application or reserved/, content
+ end
+
+ def test_check_class_collision_on_tests
+ content = capture(:stderr){ run_generator ["another_object"] }
+ assert_match /The name 'AnotherObjectHelperTest' is either already used in your application or reserved/, content
+ end
+
+ def test_namespaced_and_not_namespaced_helpers
+ run_generator ["products"]
+
+ # We have to require the generated helper to show the problem because
+ # the test helpers just check for generated files and contents but
+ # do not actually load them. But they have to be loaded (as in a real environment)
+ # to make the second generator run fail
+ require "#{destination_root}/app/helpers/products_helper"
+
+ assert_nothing_raised do
+ begin
+ run_generator ["admin::products"]
+ ensure
+ # cleanup
+ Object.send(:remove_const, :ProductsHelper)
+ end
+ end
+ end
+
+ protected
+
+ def run_generator(args=["admin"])
+ silence(:stdout) { Rails::Generators::HelperGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb
new file mode 100644
index 0000000000..190b28012c
--- /dev/null
+++ b/railties/test/generators/integration_test_generator_test.rb
@@ -0,0 +1,18 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/integration_test/integration_test_generator'
+
+class IntegrationTestGeneratorTest < GeneratorsTestCase
+
+ def test_integration_test_skeleton_is_created
+ run_generator
+ assert_file "test/integration/integration_test.rb", /class IntegrationTest < ActionController::IntegrationTest/
+ end
+
+ protected
+
+ def run_generator(args=["integration"])
+ silence(:stdout) { Rails::Generators::IntegrationTestGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
new file mode 100644
index 0000000000..9e74859c3a
--- /dev/null
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -0,0 +1,52 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/mailer/mailer_generator'
+
+class MailerGeneratorTest < GeneratorsTestCase
+
+ def test_mailer_skeleton_is_created
+ run_generator
+ assert_file "app/models/notifier.rb", /class Notifier < ActionMailer::Base/
+ end
+
+ def test_check_class_collision
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match /The name 'Object' is either already used in your application or reserved/, content
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/unit/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
+
+ def test_invokes_default_template_engine
+ run_generator
+ assert_file "app/views/notifier/foo.erb", /app\/views\/notifier\/foo/
+ assert_file "app/views/notifier/bar.erb", /app\/views\/notifier\/bar/
+ end
+
+ def test_invokes_default_template_engine_even_with_no_action
+ run_generator ["notifier"]
+ assert_file "app/views/notifier"
+ end
+
+ def test_logs_if_the_template_engine_cannot_be_found
+ content = run_generator ["notifier", "foo", "bar", "--template-engine=haml"]
+ assert_match /haml \[not found\]/, content
+ end
+
+ 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/
+ end
+
+ protected
+
+ def run_generator(args=["notifier", "foo", "bar"])
+ silence(:stdout) { Rails::Generators::MailerGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/metal_generator_test.rb b/railties/test/generators/metal_generator_test.rb
new file mode 100644
index 0000000000..97e892cefb
--- /dev/null
+++ b/railties/test/generators/metal_generator_test.rb
@@ -0,0 +1,23 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/metal/metal_generator'
+
+class MetalGeneratorTest < GeneratorsTestCase
+
+ def test_metal_skeleton_is_created
+ run_generator
+ assert_file "app/metal/foo.rb", /class Foo/
+ end
+
+ def test_check_class_collision
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match /The name 'Object' is either already used in your application or reserved/, content
+ end
+
+ protected
+
+ def run_generator(args=["foo"])
+ silence(:stdout) { Rails::Generators::MetalGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
new file mode 100644
index 0000000000..a4335068e6
--- /dev/null
+++ b/railties/test/generators/migration_generator_test.rb
@@ -0,0 +1,59 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/migration/migration_generator'
+
+class MigrationGeneratorTest < GeneratorsTestCase
+
+ def test_migration
+ @migration = "change_title_body_from_posts"
+ run_generator
+ assert_migration "db/migrate/#{@migration}.rb", /class ChangeTitleBodyFromPosts < ActiveRecord::Migration/
+ end
+
+ def test_migration_with_class_name
+ @migration = "ChangeTitleBodyFromPosts"
+ run_generator
+ assert_migration "db/migrate/change_title_body_from_posts.rb", /class #{@migration} < ActiveRecord::Migration/
+ end
+
+ def test_add_migration_with_attributes
+ @migration = "add_title_body_to_posts"
+ run_generator [@migration, "title:string", "body:text"]
+
+ assert_migration "db/migrate/#{@migration}.rb" do |content|
+ assert_class_method content, :up do |up|
+ assert_match /add_column :posts, :title, :string/, up
+ assert_match /add_column :posts, :body, :text/, up
+ end
+
+ assert_class_method content, :down do |down|
+ assert_match /remove_column :posts, :title/, down
+ assert_match /remove_column :posts, :body/, down
+ end
+ end
+ end
+
+ def test_remove_migration_with_attributes
+ @migration = "remove_title_body_from_posts"
+ run_generator [@migration, "title:string", "body:text"]
+
+ assert_migration "db/migrate/#{@migration}.rb" do |content|
+ assert_class_method content, :up do |up|
+ assert_match /remove_column :posts, :title/, up
+ assert_match /remove_column :posts, :body/, up
+ end
+
+ assert_class_method content, :down do |down|
+ assert_match /add_column :posts, :title, :string/, down
+ assert_match /add_column :posts, :body, :text/, down
+ end
+ end
+ end
+
+ protected
+
+ def run_generator(args=[@migration])
+ silence(:stdout) { Rails::Generators::MigrationGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
new file mode 100644
index 0000000000..a9b772d67b
--- /dev/null
+++ b/railties/test/generators/model_generator_test.rb
@@ -0,0 +1,137 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/model/model_generator'
+
+class ModelGeneratorTest < GeneratorsTestCase
+
+ def test_help_shows_invoked_generators_options
+ content = run_generator ["--help"]
+ assert_match /ActiveRecord options:/, content
+ assert_match /TestUnit options:/, content
+ end
+
+ def test_invokes_default_orm
+ run_generator
+ assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/
+ end
+
+ def test_model_with_parent_option
+ run_generator ["account", "--parent", "Admin::Account"]
+ assert_file "app/models/account.rb", /class Account < Admin::Account/
+ assert_no_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_model_with_underscored_parent_option
+ run_generator ["account", "--parent", "admin/account"]
+ assert_file "app/models/account.rb", /class Account < Admin::Account/
+ end
+
+ def test_migration
+ run_generator
+ assert_migration "db/migrate/create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/
+ end
+
+ def test_migration_is_skipped
+ run_generator ["account", "--no-migration"]
+ assert_no_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_migration_with_attributes
+ run_generator ["product", "name:string", "supplier_id:integer"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_class_method m, :up do |up|
+ assert_match /create_table :products/, up
+ assert_match /t\.string :name/, up
+ assert_match /t\.integer :supplier_id/, up
+ end
+
+ assert_class_method m, :down do |down|
+ assert_match /drop_table :products/, down
+ end
+ end
+ end
+
+ def test_migration_without_timestamps
+ ActiveRecord::Base.timestamped_migrations = false
+ run_generator ["account"]
+ assert_file "db/migrate/001_create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/
+
+ run_generator ["project"]
+ assert_file "db/migrate/002_create_projects.rb", /class CreateProjects < ActiveRecord::Migration/
+ ensure
+ ActiveRecord::Base.timestamped_migrations = true
+ end
+
+ def test_model_with_references_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier_id:references"]
+ assert_file "app/models/product.rb", /belongs_to :supplier/
+ end
+
+ def test_model_with_belongs_to_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier_id:belongs_to"]
+ assert_file "app/models/product.rb", /belongs_to :supplier/
+ end
+
+ def test_migration_with_timestamps
+ run_generator
+ assert_migration "db/migrate/create_accounts.rb", /t.timestamps/
+ end
+
+ def test_migration_timestamps_are_skipped
+ run_generator ["account", "--no-timestamps"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_class_method m, :up do |up|
+ assert_no_match /t.timestamps/, up
+ end
+ end
+ end
+
+ def test_migration_already_exists_error_message
+ run_generator
+ error = capture(:stderr){ run_generator ["Account"], :behavior => :skip }
+ assert_match /Another migration is already named create_accounts/, error
+ end
+
+ def test_migration_error_is_not_shown_on_revoke
+ run_generator
+ error = capture(:stderr){ run_generator ["Account"], :behavior => :revoke }
+ assert_no_match /Another migration is already named create_accounts/, error
+ end
+
+ def test_migration_is_removed_on_revoke
+ run_generator
+ run_generator ["Account"], :behavior => :revoke
+ assert_no_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/unit/account_test.rb", /class AccountTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/accounts.yml", /name: MyString/, /age: 1/
+ end
+
+ def test_fixture_is_skipped
+ run_generator ["account", "--skip-fixture"]
+ assert_no_file "test/fixtures/accounts.yml"
+ end
+
+ def test_fixture_is_skipped_if_fixture_replacement_is_given
+ content = run_generator ["account", "-r", "factory_girl"]
+ assert_match /factory_girl \[not found\]/, content
+ assert_no_file "test/fixtures/accounts.yml"
+ end
+
+ def test_check_class_collision
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match /The name 'Object' is either already used in your application or reserved/, content
+ end
+
+ protected
+
+ def run_generator(args=["Account", "name:string", "age:integer"], config={})
+ silence(:stdout) { Rails::Generators::ModelGenerator.start args, config.merge(:destination_root => destination_root) }
+ end
+
+end
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
new file mode 100644
index 0000000000..3a5c58ab32
--- /dev/null
+++ b/railties/test/generators/named_base_test.rb
@@ -0,0 +1,42 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/scaffold_controller/scaffold_controller_generator'
+
+# Mock out what we need from AR::Base.
+module ActiveRecord
+ class Base
+ class << self
+ attr_accessor :pluralize_table_names
+ end
+ self.pluralize_table_names = true
+ end
+end
+
+class NamedBaseTest < GeneratorsTestCase
+
+ def test_named_generator_attributes
+ g = Rails::Generators::ScaffoldControllerGenerator.new ["admin/foo"]
+ assert_equal 'admin/foo', g.name
+ assert_equal %w(admin), g.class_path
+ assert_equal 1, g.class_nesting_depth
+ assert_equal 'Admin::Foo', g.class_name
+ assert_equal 'foo', g.singular_name
+ assert_equal 'foos', g.plural_name
+ assert_equal g.singular_name, g.file_name
+ assert_equal "admin_#{g.plural_name}", g.table_name
+ end
+
+ def test_named_generator_attributes_without_pluralized
+ ActiveRecord::Base.pluralize_table_names = false
+ g = Rails::Generators::ScaffoldControllerGenerator.new ["admin/foo"]
+ assert_equal "admin_#{g.singular_name}", g.table_name
+ end
+
+ def test_scaffold_plural_names
+ g = Rails::Generators::ScaffoldControllerGenerator.new ["ProductLine"]
+ assert_equal "ProductLines", g.controller_name
+ assert_equal "ProductLines", g.controller_class_name
+ assert_equal "product_lines", g.controller_file_name
+ end
+
+end
diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb
new file mode 100644
index 0000000000..a556731e16
--- /dev/null
+++ b/railties/test/generators/observer_generator_test.rb
@@ -0,0 +1,33 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/observer/observer_generator'
+
+class ObserverGeneratorTest < GeneratorsTestCase
+
+ def test_invokes_default_orm
+ run_generator
+ assert_file "app/models/account_observer.rb", /class AccountObserver < ActiveRecord::Observer/
+ end
+
+ def test_invokes_default_orm_with_class_path
+ run_generator ["admin/account"]
+ assert_file "app/models/admin/account_observer.rb", /class Admin::AccountObserver < ActiveRecord::Observer/
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/unit/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/
+ end
+
+ def test_logs_if_the_test_framework_cannot_be_found
+ content = run_generator ["account", "--test-framework=rspec"]
+ assert_match /rspec \[not found\]/, content
+ end
+
+ protected
+
+ def run_generator(args=["account"])
+ silence(:stdout) { Rails::Generators::ObserverGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/performance_test_generator_test.rb b/railties/test/generators/performance_test_generator_test.rb
new file mode 100644
index 0000000000..74c9c20c4c
--- /dev/null
+++ b/railties/test/generators/performance_test_generator_test.rb
@@ -0,0 +1,18 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/performance_test/performance_test_generator'
+
+class PerformanceTestGeneratorTest < GeneratorsTestCase
+
+ def test_performance_test_skeleton_is_created
+ run_generator
+ assert_file "test/performance/performance_test.rb", /class PerformanceTest < ActionController::PerformanceTest/
+ end
+
+ protected
+
+ def run_generator(args=["performance"])
+ silence(:stdout) { Rails::Generators::PerformanceTestGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
new file mode 100644
index 0000000000..3982586bc3
--- /dev/null
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -0,0 +1,56 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/plugin/plugin_generator'
+
+class PluginGeneratorTest < GeneratorsTestCase
+
+ def test_plugin_skeleton_is_created
+ run_generator
+
+ %w(
+ vendor/plugins
+ vendor/plugins/plugin_fu
+ vendor/plugins/plugin_fu/lib
+ ).each{ |path| assert_file path }
+ end
+
+ def test_check_class_collision
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match /The name 'Object' is either already used in your application or reserved/, content
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "vendor/plugins/plugin_fu/test/plugin_fu_test.rb", /class PluginFuTest < ActiveSupport::TestCase/
+ assert_file "vendor/plugins/plugin_fu/test/test_helper.rb"
+ end
+
+ def test_logs_if_the_test_framework_cannot_be_found
+ content = run_generator ["plugin_fu", "--test-framework=rspec"]
+ assert_match /rspec \[not found\]/, content
+ end
+
+ def test_creates_tasks_if_required
+ run_generator ["plugin_fu", "--tasks"]
+ assert_file "vendor/plugins/plugin_fu/tasks/plugin_fu_tasks.rake"
+ end
+
+ def test_creates_generator_if_required
+ run_generator ["plugin_fu", "--generator"]
+ assert_file "vendor/plugins/plugin_fu/lib/generators/templates"
+ assert_file "vendor/plugins/plugin_fu/lib/generators/plugin_fu_generator.rb",
+ /class PluginFuGenerator < Rails::Generators::NamedBase/
+ end
+
+ def test_plugin_generator_on_revoke
+ run_generator
+ run_generator ["plugin_fu"], :behavior => :revoke
+ end
+
+ protected
+
+ def run_generator(args=["plugin_fu"], config={})
+ silence(:stdout) { Rails::Generators::PluginGenerator.start args, config.merge(:destination_root => destination_root) }
+ end
+
+end
diff --git a/railties/test/generators/rails_controller_generator_test.rb b/railties/test/generators/rails_controller_generator_test.rb
deleted file mode 100644
index 43fbe972e2..0000000000
--- a/railties/test/generators/rails_controller_generator_test.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'generators/generator_test_helper'
-
-module Admin
-end
-
-class RailsControllerGeneratorTest < GeneratorTestCase
-
- def test_controller_generates_controller
- run_generator('controller', %w(products))
-
- assert_generated_controller_for :products
- assert_generated_functional_test_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- end
-
- def test_controller_generates_namespaced_controller
- run_generator('controller', %w(admin::products))
-
- assert_generated_controller_for "admin::products"
- assert_generated_functional_test_for "admin::products"
- assert_generated_helper_for "admin::products"
- assert_generated_helper_test_for "admin::products"
- end
-
- def test_controller_generates_namespaced_and_not_namespaced_controllers
- run_generator('controller', %w(products))
-
- # We have to require the generated helper to show the problem because
- # the test helpers just check for generated files and contents but
- # do not actually load them. But they have to be loaded (as in a real environment)
- # to make the second generator run fail
- require "#{RAILS_ROOT}/app/helpers/products_helper"
-
- assert_nothing_raised do
- begin
- run_generator('controller', %w(admin::products))
- ensure
- # cleanup
- Object.send(:remove_const, :ProductsHelper)
- end
- end
- end
-end
diff --git a/railties/test/generators/rails_helper_generator_test.rb b/railties/test/generators/rails_helper_generator_test.rb
deleted file mode 100644
index 8d05f555e6..0000000000
--- a/railties/test/generators/rails_helper_generator_test.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require File.dirname(__FILE__) + '/generator_test_helper'
-
-class RailsHelperGeneratorTest < GeneratorTestCase
- def test_helper_generates_helper
- run_generator('helper', %w(products))
-
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- end
-
- def test_helper_generates_namespaced_helper
- run_generator('helper', %w(admin::products))
-
- assert_generated_helper_for "admin::products"
- assert_generated_helper_test_for "admin::products"
- end
-
- def test_helper_generates_namespaced_and_not_namespaced_helpers
- run_generator('helper', %w(products))
-
- # We have to require the generated helper to show the problem because
- # the test helpers just check for generated files and contents but
- # do not actually load them. But they have to be loaded (as in a real environment)
- # to make the second generator run fail
- require "#{RAILS_ROOT}/app/helpers/products_helper"
-
- assert_nothing_raised do
- begin
- run_generator('helper', %w(admin::products))
- ensure
- # cleanup
- Object.send(:remove_const, :ProductsHelper)
- end
- end
- end
-end
diff --git a/railties/test/generators/rails_mailer_generator_test.rb b/railties/test/generators/rails_mailer_generator_test.rb
deleted file mode 100644
index de61e6736d..0000000000
--- a/railties/test/generators/rails_mailer_generator_test.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'generators/generator_test_helper'
-
-class RailsMailerGeneratorTest < GeneratorTestCase
-
- def test_generates_mailer
- run_generator('mailer', %w(Notifier reset_password))
-
- assert_generated_model_for :notifier, 'ActionMailer::Base' do |model|
- assert_has_method model, :reset_password do |name, body|
- assert_equal [
- "subject 'Notifier#reset_password'",
- "recipients ''",
- "from ''",
- "sent_on sent_at",
- "",
- "body :greeting => 'Hi,'"
- ],
- body.split("\n").map{|line| line.sub(' '*4, '') }
- end
-
- assert_no_match /(self.default_url_options =|default_url_options\[.*\] =)/, model,
- 'individual mailer models should not set default_url_options because the options are shared by all mailers'
- end
-
- assert_generated_views_for :notifier, 'reset_password.erb'
- assert_generated_unit_test_for :notifier, 'ActionMailer::TestCase'
- assert_generated_file "test/fixtures/notifier/reset_password"
- end
-end
diff --git a/railties/test/generators/rails_model_generator_test.rb b/railties/test/generators/rails_model_generator_test.rb
deleted file mode 100644
index aea2abafba..0000000000
--- a/railties/test/generators/rails_model_generator_test.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require 'generators/generator_test_helper'
-
-class RailsModelGeneratorTest < GeneratorTestCase
-
- def test_model_generates_resources
- run_generator('model', %w(Product name:string))
-
- assert_generated_model_for :product
- assert_generated_fixtures_for :products
- assert_generated_migration :create_products
- end
-
- def test_model_skip_migration_skips_migration
- run_generator('model', %w(Product name:string --skip-migration))
-
- assert_generated_model_for :product
- assert_generated_fixtures_for :products
- assert_skipped_migration :create_products
- end
-
- def test_model_with_attributes_generates_resources_with_attributes
- run_generator('model', %w(Product name:string supplier_id:integer created_at:timestamp))
-
- assert_generated_model_for :product
- assert_generated_fixtures_for :products
- assert_generated_migration :create_products do |t|
- assert_generated_column t, :name, :string
- assert_generated_column t, :supplier_id, :integer
- assert_generated_column t, :created_at, :timestamp
- end
- end
-
- def test_model_with_reference_attributes_generates_belongs_to_associations
- run_generator('model', %w(Product name:string supplier:references))
-
- assert_generated_model_for :product do |body|
- assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'"
- end
- end
-
- def test_model_with_belongs_to_attributes_generates_belongs_to_associations
- run_generator('model', %w(Product name:string supplier:belongs_to))
-
- assert_generated_model_for :product do |body|
- assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'"
- end
- end
-end
diff --git a/railties/test/generators/rails_model_subclass_generator_test.rb b/railties/test/generators/rails_model_subclass_generator_test.rb
deleted file mode 100644
index 30066b5a3c..0000000000
--- a/railties/test/generators/rails_model_subclass_generator_test.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'generators/generator_test_helper'
-
-class RailsModelSubclassGeneratorTest < GeneratorTestCase
-
- def test_model_subclass_generates_resources
- run_generator('model_subclass', %w(Car Product))
-
- assert_generated_model_for :car, "Product"
- assert_generated_unit_test_for :car
- end
-
- def test_model_subclass_must_have_a_parent_class_name
- assert_raise(Rails::Generator::UsageError) { run_generator('model_subclass', %w(Car)) }
- end
-end \ No newline at end of file
diff --git a/railties/test/generators/rails_resource_generator_test.rb b/railties/test/generators/rails_resource_generator_test.rb
deleted file mode 100644
index 1f5bd0ef1e..0000000000
--- a/railties/test/generators/rails_resource_generator_test.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'generators/generator_test_helper'
-
-class RailsResourceGeneratorTest < GeneratorTestCase
- def test_resource_generates_resources
- run_generator('resource', %w(Product name:string))
-
- assert_generated_controller_for :products
- assert_generated_model_for :product
- assert_generated_fixtures_for :products
- assert_generated_functional_test_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- assert_generated_migration :create_products
- assert_added_route_for :products
- end
-
- def test_resource_skip_migration_skips_migration
- run_generator('resource', %w(Product name:string --skip-migration))
-
- assert_generated_controller_for :products
- assert_generated_model_for :product
- assert_generated_fixtures_for :products
- assert_generated_functional_test_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- assert_skipped_migration :create_products
- assert_added_route_for :products
- end
-end
diff --git a/railties/test/generators/rails_scaffold_generator_test.rb b/railties/test/generators/rails_scaffold_generator_test.rb
deleted file mode 100644
index 70829a77fd..0000000000
--- a/railties/test/generators/rails_scaffold_generator_test.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-require 'generators/generator_test_helper'
-require 'abstract_unit'
-
-class RailsScaffoldGeneratorTest < GeneratorTestCase
- def test_scaffolded_names
- g = Rails::Generator::Base.instance('scaffold', %w(ProductLine))
- assert_equal "ProductLines", g.controller_name
- assert_equal "ProductLines", g.controller_class_name
- assert_equal "ProductLine", g.controller_singular_name
- assert_equal "product_lines", g.controller_plural_name
- assert_equal "product_lines", g.controller_file_name
- assert_equal "product_lines", g.controller_table_name
- end
-
- def test_scaffold_generates_resources
-
- run_generator('scaffold', %w(Product name:string))
-
- assert_generated_controller_for :products do |f|
-
- assert_has_method f, :index do |name, m|
- assert_match /@products = Product\.all/, m, "#{name} should query products table"
- end
-
- assert_has_method f, :show, :edit, :update, :destroy do |name, m|
- assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table"
- end
-
- assert_has_method f, :new do |name, m|
- assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product"
- end
-
- assert_has_method f, :create do |name, m|
- assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product"
- assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml"
- end
-
- end
-
- assert_generated_model_for :product
- assert_generated_functional_test_for :products
- assert_generated_unit_test_for :product
- assert_generated_fixtures_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- assert_generated_stylesheet :scaffold
- assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb"
-
- assert_generated_migration :create_products
- assert_added_route_for :products
- end
-
- def test_scaffold_skip_migration_skips_migration
- run_generator('scaffold', %w(Product name:string --skip-migration))
-
- assert_generated_model_for :product
- assert_generated_functional_test_for :products
- assert_generated_unit_test_for :product
- assert_generated_fixtures_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- assert_generated_stylesheet :scaffold
- assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb"
- assert_skipped_migration :create_products
- assert_added_route_for :products
- end
-
- def test_scaffold_generates_resources_with_attributes
- run_generator('scaffold', %w(Product name:string supplier_id:integer created_at:timestamp))
-
- assert_generated_controller_for :products do |f|
-
- assert_has_method f, :index do |name, m|
- assert_match /@products = Product\.all/, m, "#{name} should query products table"
- end
-
- assert_has_method f, :show, :edit, :update, :destroy do |name, m|
- assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table"
- end
-
- assert_has_method f, :new do |name, m|
- assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product"
- end
-
- assert_has_method f, :create do |name, m|
- assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product"
- assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml"
- end
-
- end
-
- assert_generated_model_for :product
- assert_generated_functional_test_for :products
- assert_generated_unit_test_for :product
- assert_generated_fixtures_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- assert_generated_stylesheet :scaffold
- assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb"
-
- assert_generated_migration :create_products do |t|
- assert_generated_column t, :name, :string
- assert_generated_column t, :supplier_id, :integer
- assert_generated_column t, :created_at, :timestamp
- end
-
- assert_added_route_for :products
- end
-
- def test_scaffolded_plural_names
- Rails::Generator::Base.logger.expects(:warning)
- g = Rails::Generator::Base.instance('scaffold', %w(ProductLines))
- assert_equal "ProductLines", g.controller_name
- assert_equal "ProductLines", g.controller_class_name
- assert_equal "ProductLine", g.controller_singular_name
- assert_equal "product_lines", g.controller_plural_name
- assert_equal "product_lines", g.controller_file_name
- assert_equal "product_lines", g.controller_table_name
- end
-
- def test_scaffold_plural_model_name_without_force_plural_generates_singular_model
- run_generator('scaffold', %w(Products name:string))
-
- assert_generated_model_for :product
- assert_generated_functional_test_for :products
- assert_generated_unit_test_for :product
- assert_generated_fixtures_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- assert_generated_stylesheet :scaffold
- assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb"
- assert_skipped_migration :create_products
- assert_added_route_for :products
- end
-
- def test_scaffold_plural_model_name_with_force_plural_forces_plural_model
- run_generator('scaffold', %w(Products name:string --force-plural))
-
- assert_generated_model_for :products
- assert_generated_functional_test_for :products
- assert_generated_unit_test_for :products
- assert_generated_fixtures_for :products
- assert_generated_helper_for :products
- assert_generated_helper_test_for :products
- assert_generated_stylesheet :scaffold
- assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb"
- assert_skipped_migration :create_products
- assert_added_route_for :products
- end
-end
diff --git a/railties/test/generators/rails_template_runner_test.rb b/railties/test/generators/rails_template_runner_test.rb
deleted file mode 100644
index 2da6bd59b5..0000000000
--- a/railties/test/generators/rails_template_runner_test.rb
+++ /dev/null
@@ -1,216 +0,0 @@
-require 'abstract_unit'
-require 'generators/generator_test_helper'
-
-class RailsTemplateRunnerTest < GeneratorTestCase
- def setup
- Rails::Generator::Base.use_application_sources!
- run_generator('app', [RAILS_ROOT])
- # generate empty template
- @template_path = File.join(RAILS_ROOT, 'template.rb')
- File.open(File.join(@template_path), 'w') {|f| f << '' }
-
- @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git'
- @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
- end
-
- def teardown
- super
- rm_rf "#{RAILS_ROOT}/README"
- rm_rf "#{RAILS_ROOT}/Rakefile"
- rm_rf "#{RAILS_ROOT}/doc"
- rm_rf "#{RAILS_ROOT}/lib"
- rm_rf "#{RAILS_ROOT}/log"
- rm_rf "#{RAILS_ROOT}/script"
- rm_rf "#{RAILS_ROOT}/vendor"
- rm_rf "#{RAILS_ROOT}/tmp"
- rm_rf "#{RAILS_ROOT}/Capfile"
- rm_rf @template_path
- end
-
- def test_initialize_should_load_template
- Rails::TemplateRunner.any_instance.expects(:load_template).with(@template_path)
- silence_generator do
- Rails::TemplateRunner.new(@template_path, RAILS_ROOT)
- end
- end
-
- def test_initialize_should_raise_error_on_missing_template_file
- assert_raise(RuntimeError) do
- silence_generator do
- Rails::TemplateRunner.new('non/existent/path/to/template.rb', RAILS_ROOT)
- end
- end
- end
-
- def test_file_should_write_data_to_file_path
- run_template_method(:file, 'lib/test_file.rb', 'heres test data')
- assert_generated_file_with_data 'lib/test_file.rb', 'heres test data'
- end
-
- def test_file_should_write_block_contents_to_file_path
- run_template_method(:file, 'lib/test_file.rb') { 'heres block data' }
- assert_generated_file_with_data 'lib/test_file.rb', 'heres block data'
- end
-
- def test_plugin_with_git_option_should_run_plugin_install
- expects_run_ruby_script_with_command("script/plugin install #{@git_plugin_uri}")
- run_template_method(:plugin, 'restful-authentication', :git => @git_plugin_uri)
- end
-
- def test_plugin_with_svn_option_should_run_plugin_install
- expects_run_ruby_script_with_command("script/plugin install #{@svn_plugin_uri}")
- run_template_method(:plugin, 'restful-authentication', :svn => @svn_plugin_uri)
- end
-
- def test_plugin_with_git_option_and_submodule_should_use_git_scm
- Rails::Git.expects(:run).with("submodule add #{@git_plugin_uri} vendor/plugins/rest_auth")
- run_template_method(:plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true)
- end
-
- def test_plugin_with_no_options_should_skip_method
- Rails::TemplateRunner.any_instance.expects(:run).never
- run_template_method(:plugin, 'rest_auth', {})
- end
-
- def test_gem_should_put_gem_dependency_in_enviroment
- run_template_method(:gem, 'will-paginate')
- assert_rails_initializer_includes("config.gem 'will-paginate'")
- end
-
- def test_gem_with_options_should_include_options_in_gem_dependency_in_environment
- run_template_method(:gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com')
- assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'")
- end
-
- def test_gem_with_env_string_should_put_gem_dependency_in_specified_environment
- run_template_method(:gem, 'rspec', :env => 'test')
- assert_generated_file_with_data('config/environments/test.rb', "config.gem 'rspec'", 'test')
- end
-
- def test_gem_with_env_array_should_put_gem_dependency_in_specified_environments
- run_template_method(:gem, 'quietbacktrace', :env => %w[ development test ])
- assert_generated_file_with_data('config/environments/development.rb', "config.gem 'quietbacktrace'")
- assert_generated_file_with_data('config/environments/test.rb', "config.gem 'quietbacktrace'")
- end
-
- def test_gem_with_lib_option_set_to_false_should_put_gem_dependency_in_enviroment_correctly
- run_template_method(:gem, 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com')
- assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com'")
- end
-
- def test_environment_should_include_data_in_environment_initializer_block
- load_paths = 'config.load_paths += %w["#{RAILS_ROOT}/app/extras"]'
- run_template_method(:environment, load_paths)
- assert_rails_initializer_includes(load_paths)
- end
-
- def test_environment_with_block_should_include_block_contents_in_environment_initializer_block
- run_template_method(:environment) do
- '# This wont be added'
- '# This will be added'
- end
- assert_rails_initializer_includes('# This will be added')
- end
-
- def test_git_with_symbol_should_run_command_using_git_scm
- Rails::Git.expects(:run).once.with('init')
- run_template_method(:git, :init)
- end
-
- def test_git_with_hash_should_run_each_command_using_git_scm
- Rails::Git.expects(:run).times(2)
- run_template_method(:git, {:init => '', :add => '.'})
- end
-
- def test_vendor_should_write_data_to_file_in_vendor
- run_template_method(:vendor, 'vendor_file.rb', '# vendor data')
- assert_generated_file_with_data('vendor/vendor_file.rb', '# vendor data')
- end
-
- def test_lib_should_write_data_to_file_in_lib
- run_template_method(:lib, 'my_library.rb', 'class MyLibrary')
- assert_generated_file_with_data('lib/my_library.rb', 'class MyLibrary')
- end
-
- def test_rakefile_should_write_date_to_file_in_lib_tasks
- run_template_method(:rakefile, 'myapp.rake', 'task :run => [:environment]')
- assert_generated_file_with_data('lib/tasks/myapp.rake', 'task :run => [:environment]')
- end
-
- def test_initializer_should_write_date_to_file_in_config_initializers
- run_template_method(:initializer, 'constants.rb', 'MY_CONSTANT = 42')
- assert_generated_file_with_data('config/initializers/constants.rb', 'MY_CONSTANT = 42')
- end
-
- def test_generate_should_run_script_generate_with_argument_and_options
- expects_run_ruby_script_with_command('script/generate model MyModel')
- run_template_method(:generate, 'model', 'MyModel')
- end
-
- def test_rake_should_run_rake_command_with_development_env
- expects_run_with_command('rake log:clear RAILS_ENV=development')
- run_template_method(:rake, 'log:clear')
- end
-
- def test_rake_with_env_option_should_run_rake_command_in_env
- expects_run_with_command('rake log:clear RAILS_ENV=production')
- run_template_method(:rake, 'log:clear', :env => 'production')
- end
-
- def test_rake_with_sudo_option_should_run_rake_command_with_sudo
- expects_run_with_command('sudo rake log:clear RAILS_ENV=development')
- run_template_method(:rake, 'log:clear', :sudo => true)
- end
-
- def test_capify_should_run_the_capify_command
- expects_run_with_command('capify .')
- run_template_method(:capify!)
- end
-
- def test_freeze_should_freeze_rails_edge
- expects_run_with_command('rake rails:freeze:edge')
- run_template_method(:freeze!)
- end
-
- def test_route_should_add_data_to_the_routes_block_in_config_routes
- route_command = "map.route '/login', :controller => 'sessions', :action => 'new'"
- run_template_method(:route, route_command)
- assert_generated_file_with_data 'config/routes.rb', route_command
- end
-
- def test_run_ruby_script_should_add_ruby_to_command_in_win32_environment
- ruby_command = RUBY_PLATFORM =~ /win32/ ? 'ruby ' : ''
- expects_run_with_command("#{ruby_command}script/generate model MyModel")
- run_template_method(:generate, 'model', 'MyModel')
- end
-
- protected
- def run_template_method(method_name, *args, &block)
- silence_generator do
- @template_runner = Rails::TemplateRunner.new(@template_path, RAILS_ROOT)
- @template_runner.send(method_name, *args, &block)
- end
- end
-
- def expects_run_with_command(command)
- Rails::TemplateRunner.any_instance.stubs(:run).once.with(command, false)
- end
-
- def expects_run_ruby_script_with_command(command)
- Rails::TemplateRunner.any_instance.stubs(:run_ruby_script).once.with(command,false)
- end
-
- def assert_rails_initializer_includes(data, message = nil)
- message ||= "Rails::Initializer should include #{data}"
- assert_generated_file 'config/environment.rb' do |body|
- assert_match(/#{Regexp.escape("Rails::Initializer.run do |config|")}.+#{Regexp.escape(data)}.+end/m, body, message)
- end
- end
-
- def assert_generated_file_with_data(file, data, message = nil)
- message ||= "#{file} should include '#{data}'"
- assert_generated_file(file) do |file|
- assert_match(/#{Regexp.escape(data)}/,file, message)
- end
- end
-end \ No newline at end of file
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
new file mode 100644
index 0000000000..876ddbf84e
--- /dev/null
+++ b/railties/test/generators/resource_generator_test.rb
@@ -0,0 +1,106 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/resource/resource_generator'
+
+class ResourceGeneratorTest < GeneratorsTestCase
+
+ def setup
+ super
+ routes = Rails::Generators::ResourceGenerator.source_root
+ routes = File.join(routes, "..", "..", "app", "templates", "config", "routes.rb")
+ destination = File.join(destination_root, "config")
+
+ FileUtils.mkdir_p(destination)
+ FileUtils.cp File.expand_path(routes), destination
+ end
+
+ def test_help_with_inherited_options
+ content = run_generator ["--help"]
+ assert_match /ActiveRecord options:/, content
+ assert_match /TestUnit options:/, content
+ end
+
+ def test_files_from_inherited_invocation
+ run_generator
+
+ %w(
+ app/models/account.rb
+ test/unit/account_test.rb
+ test/fixtures/accounts.yml
+ ).each { |path| assert_file path }
+
+ assert_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_inherited_invocations_with_attributes
+ run_generator ["account", "name:string"]
+ assert_migration "db/migrate/create_accounts.rb", /t.string :name/
+ end
+
+ def test_resource_controller_with_pluralized_class_name
+ run_generator
+ assert_file "app/controllers/accounts_controller.rb", /class AccountsController < ApplicationController/
+ assert_file "test/functional/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/
+
+ assert_file "app/helpers/accounts_helper.rb", /module AccountsHelper/
+ assert_file "test/unit/helpers/accounts_helper_test.rb", /class AccountsHelperTest < ActionView::TestCase/
+ end
+
+ def test_resource_controller_with_actions
+ run_generator ["account", "--actions", "index", "new"]
+
+ assert_file "app/controllers/accounts_controller.rb" do |controller|
+ assert_instance_method controller, :index
+ assert_instance_method controller, :new
+ end
+
+ assert_file "app/views/accounts/index.html.erb"
+ assert_file "app/views/accounts/new.html.erb"
+ end
+
+ def test_resource_routes_are_added
+ run_generator
+
+ assert_file "config/routes.rb" do |route|
+ assert_match /map\.resources :accounts$/, route
+ end
+ end
+
+ def test_singleton_resource
+ run_generator ["account", "--singleton"]
+
+ assert_file "config/routes.rb" do |route|
+ assert_match /map\.resource :account$/, route
+ end
+ end
+
+ def test_plural_names_are_singularized
+ content = run_generator ["accounts"]
+ assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/
+ assert_file "test/unit/account_test.rb", /class AccountTest/
+ assert_match /Plural version of the model detected, using singularized version. Override with --force-plural./, content
+ end
+
+ def test_plural_names_can_be_forced
+ content = run_generator ["accounts", "--force-plural"]
+ assert_file "app/models/accounts.rb", /class Accounts < ActiveRecord::Base/
+ assert_file "test/unit/accounts_test.rb", /class AccountsTest/
+ assert_no_match /Plural version of the model detected/, content
+ end
+
+ def test_route_is_removed_on_revoke
+ run_generator
+ run_generator ["account"], :behavior => :revoke
+
+ assert_file "config/routes.rb" do |route|
+ assert_no_match /map\.resources :accounts$/, route
+ end
+ end
+
+ protected
+
+ def run_generator(args=["account"], config={})
+ silence(:stdout) { Rails::Generators::ResourceGenerator.start args, config.merge(:destination_root => destination_root) }
+ end
+
+end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
new file mode 100644
index 0000000000..024ea439ef
--- /dev/null
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -0,0 +1,112 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/scaffold_controller/scaffold_controller_generator'
+
+class ScaffoldControllerGeneratorTest < GeneratorsTestCase
+
+ def test_controller_skeleton_is_created
+ run_generator
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match /class UsersController < ApplicationController/, content
+
+ assert_instance_method content, :index do |m|
+ assert_match /@users = User\.all/, m
+ end
+
+ assert_instance_method content, :show do |m|
+ assert_match /@user = User\.find\(params\[:id\]\)/, m
+ end
+
+ assert_instance_method content, :new do |m|
+ assert_match /@user = User\.new/, m
+ end
+
+ assert_instance_method content, :edit do |m|
+ assert_match /@user = User\.find\(params\[:id\]\)/, m
+ end
+
+ assert_instance_method content, :create do |m|
+ assert_match /@user = User\.new\(params\[:user\]\)/, m
+ assert_match /@user\.save/, m
+ assert_match /@user\.errors/, m
+ end
+
+ assert_instance_method content, :update do |m|
+ assert_match /@user = User\.find\(params\[:id\]\)/, m
+ assert_match /@user\.update_attributes\(params\[:user\]\)/, m
+ assert_match /@user\.errors/, m
+ end
+
+ assert_instance_method content, :destroy do |m|
+ assert_match /@user = User\.find\(params\[:id\]\)/, m
+ assert_match /@user\.destroy/, m
+ end
+ end
+ end
+
+ def test_helper_are_invoked_with_a_pluralized_name
+ run_generator
+ assert_file "app/helpers/users_helper.rb", /module UsersHelper/
+ assert_file "test/unit/helpers/users_helper_test.rb", /class UsersHelperTest < ActionView::TestCase/
+ end
+
+ def test_views_are_generated
+ run_generator
+
+ %w(
+ index
+ edit
+ new
+ show
+ ).each { |view| assert_file "app/views/users/#{view}.html.erb" }
+ assert_file "app/views/layouts/users.html.erb"
+ end
+
+ def test_functional_tests
+ run_generator
+
+ assert_file "test/functional/users_controller_test.rb" do |content|
+ assert_match /class UsersControllerTest < ActionController::TestCase/, content
+ assert_match /test "should get index"/, content
+ end
+ end
+
+ def test_generates_singleton_controller
+ run_generator ["User", "name:string", "age:integer", "--singleton"]
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_no_match /def index/, content
+ end
+
+ assert_file "test/functional/users_controller_test.rb" do |content|
+ assert_no_match /test "should get index"/, content
+ end
+
+ assert_no_file "app/views/users/index.html.erb"
+ end
+
+ def test_skip_helper_if_required
+ run_generator ["User", "name:string", "age:integer", "--no-helper"]
+ assert_no_file "app/helpers/users_helper.rb"
+ assert_no_file "test/unit/helpers/users_helper_test.rb"
+ end
+
+ def test_skip_layout_if_required
+ run_generator ["User", "name:string", "age:integer", "--no-layout"]
+ assert_no_file "app/views/layouts/users.html.erb"
+ end
+
+ def test_error_is_shown_if_orm_does_not_provide_interface
+ error = capture(:stderr){ run_generator ["User", "--orm=unknown"] }
+ assert_equal "Could not load Unknown::Generators::ActionORM, skipping controller. " <<
+ "Error: no such file to load -- generators/unknown.\n", error
+ end
+
+ protected
+
+ def run_generator(args=["User", "name:string", "age:integer"])
+ silence(:stdout) { Rails::Generators::ScaffoldControllerGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
new file mode 100644
index 0000000000..05eadd3460
--- /dev/null
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -0,0 +1,129 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/scaffold/scaffold_generator'
+
+class ScaffoldGeneratorTest < GeneratorsTestCase
+
+ def setup
+ super
+ routes = Rails::Generators::ResourceGenerator.source_root
+ routes = File.join(routes, "..", "..", "app", "templates", "config", "routes.rb")
+ destination = File.join(destination_root, "config")
+
+ FileUtils.mkdir_p(destination)
+ FileUtils.cp File.expand_path(routes), destination
+ end
+
+ def test_scaffold_on_invoke
+ run_generator
+
+ # Model
+ assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
+ assert_file "test/unit/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/product_lines.yml"
+ assert_migration "db/migrate/create_product_lines.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match /map\.resources :product_lines$/, route
+ end
+
+ # Controller
+ assert_file "app/controllers/product_lines_controller.rb" do |content|
+ assert_match /class ProductLinesController < ApplicationController/, content
+
+ assert_instance_method content, :index do |m|
+ assert_match /@product_lines = ProductLine\.all/, m
+ end
+
+ assert_instance_method content, :show do |m|
+ assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
+ end
+
+ assert_instance_method content, :new do |m|
+ assert_match /@product_line = ProductLine\.new/, m
+ end
+
+ assert_instance_method content, :edit do |m|
+ assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
+ end
+
+ assert_instance_method content, :create do |m|
+ assert_match /@product_line = ProductLine\.new\(params\[:product_line\]\)/, m
+ assert_match /@product_line\.save/, m
+ assert_match /@product_line\.errors/, m
+ end
+
+ assert_instance_method content, :update do |m|
+ assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
+ assert_match /@product_line\.update_attributes\(params\[:product_line\]\)/, m
+ assert_match /@product_line\.errors/, m
+ end
+
+ assert_instance_method content, :destroy do |m|
+ assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
+ assert_match /@product_line\.destroy/, m
+ end
+ end
+
+ assert_file "test/functional/product_lines_controller_test.rb",
+ /class ProductLinesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(
+ index
+ edit
+ new
+ show
+ ).each { |view| assert_file "app/views/product_lines/#{view}.html.erb" }
+ assert_file "app/views/layouts/product_lines.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/product_lines_helper.rb"
+ assert_file "test/unit/helpers/product_lines_helper_test.rb"
+
+ # Stylesheets
+ assert_file "public/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_on_revoke
+ run_generator
+ run_generator :behavior => :revoke
+
+ # Model
+ assert_no_file "app/models/product_line.rb"
+ assert_no_file "test/unit/product_line_test.rb"
+ assert_no_file "test/fixtures/product_lines.yml"
+ assert_no_migration "db/migrate/create_product_lines.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match /map\.resources :product_lines$/, route
+ end
+
+ # Controller
+ assert_no_file "app/controllers/product_lines_controller.rb"
+ assert_no_file "test/functional/product_lines_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/product_lines"
+ assert_no_file "app/views/layouts/product_lines.html.erb"
+
+ # Helpers
+ assert_no_file "app/helpers/product_lines_helper.rb"
+ assert_no_file "test/unit/helpers/product_lines_helper_test.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "public/stylesheets/scaffold.css"
+ end
+
+ protected
+
+ def run_generator(config={})
+ silence(:stdout) do
+ Rails::Generators::ScaffoldGenerator.start ["product_line", "title:string", "price:integer"],
+ config.merge(:destination_root => destination_root)
+ end
+ end
+
+end
diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb
new file mode 100644
index 0000000000..f83109800b
--- /dev/null
+++ b/railties/test/generators/session_migration_generator_test.rb
@@ -0,0 +1,23 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/session_migration/session_migration_generator'
+
+class SessionMigrationGeneratorTest < GeneratorsTestCase
+
+ def test_session_migration_with_default_name
+ run_generator
+ assert_migration "db/migrate/add_sessions_table.rb", /class AddSessionsTable < ActiveRecord::Migration/
+ end
+
+ def test_session_migration_with_given_name
+ run_generator ["create_session_table"]
+ assert_migration "db/migrate/create_session_table.rb", /class CreateSessionTable < ActiveRecord::Migration/
+ end
+
+ protected
+
+ def run_generator(args=[])
+ silence(:stdout) { Rails::Generators::SessionMigrationGenerator.start args, :destination_root => destination_root }
+ end
+
+end
diff --git a/railties/test/generators/stylesheets_generator_test.rb b/railties/test/generators/stylesheets_generator_test.rb
new file mode 100644
index 0000000000..02082d5cc8
--- /dev/null
+++ b/railties/test/generators/stylesheets_generator_test.rb
@@ -0,0 +1,24 @@
+require 'abstract_unit'
+require 'generators/generators_test_helper'
+require 'generators/rails/stylesheets/stylesheets_generator'
+
+class StylesheetsGeneratorTest < GeneratorsTestCase
+
+ def test_copy_stylesheets
+ run_generator
+ assert_file "public/stylesheets/scaffold.css"
+ end
+
+ def test_stylesheets_are_not_deleted_on_revoke
+ run_generator
+ run_generator :behavior => :revoke
+ assert_file "public/stylesheets/scaffold.css"
+ end
+
+ protected
+
+ def run_generator(config={})
+ silence(:stdout) { Rails::Generators::StylesheetsGenerator.start [], config.merge(:destination_root => destination_root) }
+ end
+
+end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
new file mode 100644
index 0000000000..89d52dd170
--- /dev/null
+++ b/railties/test/generators_test.rb
@@ -0,0 +1,156 @@
+require File.join(File.dirname(__FILE__), 'generators', 'generators_test_helper')
+require 'generators/rails/model/model_generator'
+require 'generators/test_unit/model/model_generator'
+require 'mocha'
+
+class GeneratorsTest < GeneratorsTestCase
+ def test_invoke_when_generator_is_not_found
+ output = capture(:stdout){ Rails::Generators.invoke :unknown }
+ assert_equal "Could not find generator unknown.\n", output
+ end
+
+ def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments
+ output = capture(:stdout){ Rails::Generators.invoke :model, [] }
+ assert_match /Description:/, output
+ end
+
+ def test_invoke_with_default_values
+ Rails::Generators::ModelGenerator.expects(:start).with(["Account"], {})
+ Rails::Generators.invoke :model, ["Account"]
+ end
+
+ def test_invoke_with_config_values
+ Rails::Generators::ModelGenerator.expects(:start).with(["Account"], :behavior => :skip)
+ 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)
+ 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
+ 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_add_generators_to_raw_lookups
+ klass = Rails::Generators.find_by_namespace("test_unit:model")
+ assert klass
+ assert_equal "test_unit:generators:model", klass.namespace
+ end
+
+ def test_find_by_namespace_lookup_to_the_rails_root_folder
+ 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
+ klass = Rails::Generators.find_by_namespace(:fixjour, :active_record)
+ assert klass
+ assert_equal "active_record:generators:fixjour", klass.namespace
+ end
+
+ def test_find_by_namespace_lookup_traverse_folders
+ klass = Rails::Generators.find_by_namespace(:javascripts, :rails)
+ assert klass
+ assert_equal "rails:generators:javascripts", 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
+ end
+
+ def test_builtin_generators
+ assert Rails::Generators.builtin.include? %w(rails model)
+ end
+
+ def test_rails_generators_help_with_builtin_information
+ output = capture(:stdout){ Rails::Generators.help }
+ 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, mspec, rails:javascripts, wrong.", output
+ end
+
+ def test_warning_is_shown_if_generator_cant_be_loaded
+ output = capture(:stderr){ Rails::Generators.find_by_namespace(:wrong) }
+ assert_match /\[WARNING\] Could not load generator at/, output
+ assert_match /Error: uninitialized constant Rails::Generator/, output
+ end
+
+ def test_no_color_sets_proper_shell
+ Rails::Generators.no_color!
+ assert_equal Thor::Shell::Basic, Thor::Base.shell
+ ensure
+ 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
+ end
+
+ def test_fallbacks_for_generators_on_invoke
+ Rails::Generators.fallbacks[:shoulda] = :test_unit
+ TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
+ Rails::Generators.invoke "shoulda:model", ["Account"]
+ end
+
+ def test_nested_fallbacks_for_generators
+ Rails::Generators.fallbacks[:super_shoulda] = :shoulda
+ TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
+ Rails::Generators.invoke "super_shoulda:model", ["Account"]
+ end
+
+ def test_developer_options_are_overwriten_by_user_options
+ Rails::Generators.options[:new_generator] = { :generate => false }
+
+ klass = Class.new(Rails::Generators::Base) do
+ def self.name
+ "NewGenerator"
+ end
+
+ class_option :generate, :default => true
+ end
+
+ assert_equal false, klass.class_options[:generate].default
+ 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"))
+ end
+end
diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb
index 550cb7de76..1fecd62995 100644
--- a/railties/test/initializer_test.rb
+++ b/railties/test/initializer_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'initializer'
+require 'generators'
require 'action_view'
require 'action_mailer'
@@ -10,8 +11,19 @@ module Rails
def self.configuration
Rails::Configuration.new
end
+
+ module Generators
+ def self.clear_aliases!
+ @aliases = nil
+ end
+
+ def self.clear_options!
+ @@options = nil
+ end
+ end
end
+
class ConfigurationMock < Rails::Configuration
attr_reader :environment_path
@@ -278,6 +290,87 @@ class InitializerPluginLoadingTests < Test::Unit::TestCase
end
end
+class InitializerGeneratorsTests < Test::Unit::TestCase
+
+ def setup
+ @configuration = Rails::Configuration.new
+ @initializer = Rails::Initializer.default
+ @initializer.config = @configuration
+ end
+
+ def test_generators_default_values
+ assert_equal(true, @configuration.generators.colorize_logging)
+ assert_equal({}, @configuration.generators.aliases)
+ assert_equal({}, @configuration.generators.options)
+ end
+
+ def test_generators_set_rails_options
+ @configuration.generators.orm = :datamapper
+ @configuration.generators.test_framework = :rspec
+ expected = { :rails => { :orm => :datamapper, :test_framework => :rspec } }
+ assert_equal expected, @configuration.generators.options
+ end
+
+ def test_generators_set_rails_aliases
+ @configuration.generators.aliases = { :rails => { :test_framework => "-w" } }
+ expected = { :rails => { :test_framework => "-w" } }
+ assert_equal expected, @configuration.generators.aliases
+ end
+
+ def test_generators_aliases_and_options_on_initialization
+ @configuration.generators.rails :aliases => { :test_framework => "-w" }
+ @configuration.generators.orm :datamapper
+ @configuration.generators.test_framework :rspec
+
+ @initializer.run(:initialize_generators)
+
+ assert_equal :rspec, Rails::Generators.options[:rails][:test_framework]
+ assert_equal "-w", Rails::Generators.aliases[:rails][:test_framework]
+ end
+
+ def test_generators_no_color_on_initialization
+ @configuration.generators.colorize_logging = false
+ @initializer.run(:initialize_generators)
+ assert_equal Thor::Base.shell, Thor::Shell::Basic
+ end
+
+ def test_generators_with_hashes_for_options_and_aliases
+ @configuration.generators do |g|
+ g.orm :datamapper, :migration => false
+ g.plugin :aliases => { :generator => "-g" },
+ :generator => true
+ end
+
+ expected = {
+ :rails => { :orm => :datamapper },
+ :plugin => { :generator => true },
+ :datamapper => { :migration => false }
+ }
+
+ assert_equal expected, @configuration.generators.options
+ assert_equal({ :plugin => { :generator => "-g" } }, @configuration.generators.aliases)
+ end
+
+ def test_generators_with_hashes_are_deep_merged
+ @configuration.generators do |g|
+ g.orm :datamapper, :migration => false
+ g.plugin :aliases => { :generator => "-g" },
+ :generator => true
+ end
+ @initializer.run(:initialize_generators)
+
+ assert Rails::Generators.aliases.size >= 1
+ assert Rails::Generators.options.size >= 1
+ end
+
+ protected
+
+ def teardown
+ Rails::Generators.clear_aliases!
+ Rails::Generators.clear_options!
+ end
+end
+
class InitializerSetupI18nTests < Test::Unit::TestCase
def test_no_config_locales_dir_present_should_return_empty_load_path
File.stubs(:exist?).returns(false)
diff --git a/railties/test/metal_test.rb b/railties/test/metal_test.rb
index d3d231132b..c79a819a76 100644
--- a/railties/test/metal_test.rb
+++ b/railties/test/metal_test.rb
@@ -55,8 +55,38 @@ class MetalTest < Test::Unit::TestCase
assert_equal(["FooMetal", "EngineMetal"], found_metals_as_string_array)
end
end
+
+ def test_metal_default_pass_through_on_404
+ use_appdir("multiplemetals") do
+ result = Rails::Rack::Metal.new(app).call({})
+ assert_equal 200, result.first
+ assert_equal ["Metal B"], result.last
+ end
+ end
+
+ def test_metal_pass_through_on_417
+ use_appdir("multiplemetals") do
+ Rails::Rack::Metal.pass_through_on = 417
+ result = Rails::Rack::Metal.new(app).call({})
+ assert_equal 404, result.first
+ assert_equal ["Metal A"], result.last
+ end
+ end
+
+ def test_metal_pass_through_on_404_and_200
+ use_appdir("multiplemetals") do
+ Rails::Rack::Metal.pass_through_on = [404, 200]
+ result = Rails::Rack::Metal.new(app).call({})
+ assert_equal 402, result.first
+ assert_equal ["End of the Line"], result.last
+ end
+ end
private
+
+ def app
+ lambda{[402,{},["End of the Line"]]}
+ end
def use_appdir(root)
dir = "#{File.dirname(__FILE__)}/fixtures/metal/#{root}"
diff --git a/railties/test/rails_generator_test.rb b/railties/test/rails_generator_test.rb
deleted file mode 100644
index 38bd90dcc1..0000000000
--- a/railties/test/rails_generator_test.rb
+++ /dev/null
@@ -1,148 +0,0 @@
-require 'test/unit'
-
-# Optionally load RubyGems.
-begin
- require 'rubygems'
-rescue LoadError
-end
-
-# Mock out what we need from AR::Base.
-module ActiveRecord
- class Base
- class << self
- attr_accessor :pluralize_table_names
- end
- self.pluralize_table_names = true
- end
-end
-
-# And what we need from ActionView
-module ActionView
- module Helpers
- module ActiveRecordHelper; end
- class InstanceTag; end
- end
-end
-
-
-# Must set before requiring generator libs.
-if defined?(RAILS_ROOT)
- RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures"
-else
- RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures"
-end
-
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
-require 'initializer'
-
-# Mocks out the configuration
-module Rails
- def self.configuration
- Rails::Configuration.new
- end
-end
-
-require 'rails_generator'
-
-class RailsGeneratorTest < Test::Unit::TestCase
- BUILTINS = %w(controller integration_test mailer migration model observer plugin resource scaffold session_migration)
- CAPITALIZED_BUILTINS = BUILTINS.map { |b| b.capitalize }
-
- def setup
- ActiveRecord::Base.pluralize_table_names = true
- @initializer = Rails::Initializer.default
- @initializer.config = Rails.configuration
- @initializer.run(:set_root_path)
- end
-
- def test_sources
- expected = [:lib, :vendor,
- "plugins (vendor/plugins)".to_sym, # <plugin>/generators and <plugin>/rails_generators
- :user,
- :RubyGems, :RubyGems, # gems named <x>_generator, gems containing /rails_generator/ folder
- :builtin]
- expected.delete(:RubyGems) unless Object.const_defined?(:Gem)
- assert_equal expected, Rails::Generator::Base.sources.map { |s| s.label }
- end
-
- def test_lookup_builtins
- (BUILTINS + CAPITALIZED_BUILTINS).each do |name|
- assert_nothing_raised do
- spec = Rails::Generator::Base.lookup(name)
- assert_not_nil spec
- assert_kind_of Rails::Generator::Spec, spec
-
- klass = spec.klass
- assert klass < Rails::Generator::Base
- assert_equal spec, klass.spec
- end
- end
- end
-
- def test_autolookup
- assert_nothing_raised { ControllerGenerator }
- assert_nothing_raised { ModelGenerator }
- end
-
- def test_lookup_missing_generator
- assert_raise Rails::Generator::GeneratorError do
- Rails::Generator::Base.lookup('missing').klass
- end
- end
-
- def test_lookup_missing_class
- spec = nil
- assert_nothing_raised { spec = Rails::Generator::Base.lookup('missing_class') }
- assert_not_nil spec
- assert_kind_of Rails::Generator::Spec, spec
- assert_raise(NameError) { spec.klass }
- end
-
- def test_generator_usage
- (BUILTINS - ["session_migration"]).each do |name|
- assert_raise(Rails::Generator::UsageError, "Generator '#{name}' should raise an error without arguments") {
- Rails::Generator::Base.instance(name)
- }
- end
- end
-
- def test_generator_spec
- spec = Rails::Generator::Base.lookup('working')
- assert_equal 'working', spec.name
- assert_match(/#{spec.path}$/, "#{RAILS_ROOT}/lib/generators/working")
- assert_equal :lib, spec.source
- assert_nothing_raised { assert_match(/WorkingGenerator$/, spec.klass.name) }
- end
-
- def test_named_generator_attributes
- g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz))
- assert_equal 'admin/foo', g.name
- assert_equal %w(admin), g.class_path
- assert_equal 'Admin', g.class_nesting
- assert_equal 'Admin::Foo', g.class_name
- assert_equal 'foo', g.singular_name
- assert_equal 'foos', g.plural_name
- assert_equal g.singular_name, g.file_name
- assert_equal "admin_#{g.plural_name}", g.table_name
- assert_equal %w(bar baz), g.args
- end
-
- def test_named_generator_attributes_without_pluralized
- ActiveRecord::Base.pluralize_table_names = false
- g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz))
- assert_equal "admin_#{g.singular_name}", g.table_name
- end
-
- def test_session_migration_generator_with_pluralization
- g = Rails::Generator::Base.instance('session_migration')
- assert_equal 'session'.pluralize, g.send(:default_session_table_name)
- ActiveRecord::Base.pluralize_table_names = false
- assert_equal 'session', g.send(:default_session_table_name)
- end
-
- def test_scaffold_controller_name
- # Default behaviour is use the model name
- g = Rails::Generator::Base.instance('scaffold', %w(Product))
- assert_equal "Products", g.controller_name
- end
-end
diff --git a/railties/test/secret_key_generation_test.rb b/railties/test/secret_key_generation_test.rb
deleted file mode 100644
index 2c7c3d5dfe..0000000000
--- a/railties/test/secret_key_generation_test.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require 'abstract_unit'
-
-# Must set before requiring generator libs.
-if defined?(RAILS_ROOT)
- RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures"
-else
- RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures"
-end
-
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
-
-require 'initializer'
-
-# Mocks out the configuration
-module Rails
- def self.configuration
- Rails::Configuration.new
- end
-end
-
-require 'rails_generator'
-require 'rails_generator/secret_key_generator'
-require 'rails_generator/generators/applications/app/app_generator'
-
-class SecretKeyGenerationTest < ActiveSupport::TestCase
- SECRET_KEY_MIN_LENGTH = 128
- APP_NAME = "foo"
-
- def setup
- @generator = Rails::SecretKeyGenerator.new(APP_NAME)
- end
-
- def test_secret_key_generation
- assert_deprecated /ActiveSupport::SecureRandom\.hex\(64\)/ do
- assert @generator.generate_secret.length >= SECRET_KEY_MIN_LENGTH
- end
- end
-end