aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile3
-rw-r--r--README.rdoc5
-rwxr-xr-xRakefile3
-rw-r--r--actionpack/CHANGELOG34
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb2
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb9
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb2
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb5
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb18
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb4
-rw-r--r--actionpack/lib/action_view/asset_paths.rb2
-rw-r--r--actionpack/lib/action_view/base.rb2
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb56
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb14
-rw-r--r--actionpack/lib/sprockets/assets.rake52
-rw-r--r--actionpack/lib/sprockets/compressors.rb30
-rw-r--r--actionpack/lib/sprockets/helpers/rails_helper.rb49
-rw-r--r--actionpack/lib/sprockets/railtie.rb12
-rw-r--r--actionpack/test/abstract_unit.rb6
-rw-r--r--actionpack/test/controller/assert_select_test.rb19
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb8
-rw-r--r--actionpack/test/controller/render_test.rb2
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb16
-rw-r--r--actionpack/test/controller/resources_test.rb11
-rw-r--r--actionpack/test/dispatch/cookies_test.rb16
-rw-r--r--actionpack/test/fixtures/test/hello,world.erb (renamed from actionpack/test/fixtures/test/hello_w*rld.erb)0
-rw-r--r--actionpack/test/template/capture_helper_test.rb36
-rw-r--r--actionpack/test/template/form_helper_test.rb3
-rw-r--r--actionpack/test/template/record_tag_helper_test.rb41
-rw-r--r--actionpack/test/template/sprockets_helper_test.rb6
-rw-r--r--actionpack/test/template/url_helper_test.rb10
-rw-r--r--activemodel/activemodel.gemspec1
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activemodel/lib/active_model/errors.rb32
-rw-r--r--activemodel/lib/active_model/secure_password.rb11
-rw-r--r--activemodel/test/cases/errors_test.rb37
-rw-r--r--activerecord/CHANGELOG12
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb39
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb62
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb44
-rw-r--r--activerecord/lib/active_record/connection_adapters/statement_pool.rb40
-rw-r--r--activerecord/lib/active_record/fixtures.rb729
-rw-r--r--activerecord/lib/active_record/fixtures/file.rb12
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb2
-rw-r--r--activerecord/lib/active_record/railties/databases.rake12
-rw-r--r--activerecord/lib/active_record/reflection.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb43
-rw-r--r--activerecord/lib/active_record/relation/batches.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql/statement_pool_test.rb23
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/statement_pool_test.rb23
-rw-r--r--activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb24
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/extension_test.rb10
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb10
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb11
-rw-r--r--activerecord/test/cases/base_test.rb56
-rw-r--r--activerecord/test/cases/batches_test.rb20
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb21
-rw-r--r--activerecord/test/cases/finder_test.rb35
-rw-r--r--activerecord/test/cases/fixtures_test.rb16
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb4
-rw-r--r--activerecord/test/cases/named_scope_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb130
-rw-r--r--activerecord/test/fixtures/tasks.yml2
-rw-r--r--activerecord/test/models/car.rb2
-rw-r--r--activerecord/test/models/post.rb10
-rw-r--r--activeresource/lib/active_resource/base.rb2
-rw-r--r--activeresource/test/cases/base/load_test.rb19
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb22
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb4
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb5
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb2
-rw-r--r--activesupport/test/core_ext/module_test.rb10
-rw-r--r--activesupport/test/ordered_hash_test.rb5
-rwxr-xr-xbin/rails7
-rw-r--r--rails.gemspec2
-rw-r--r--railties/README.rdoc8
-rwxr-xr-xrailties/Rakefile9
-rwxr-xr-xrailties/bin/rails7
-rw-r--r--railties/guides/code/getting_started/Gemfile32
-rw-r--r--railties/guides/code/getting_started/README261
-rw-r--r--railties/guides/code/getting_started/Rakefile7
-rw-r--r--railties/guides/code/getting_started/app/assets/images/rails.pngbin0 -> 6646 bytes
-rw-r--r--railties/guides/code/getting_started/app/assets/javascripts/application.js9
-rw-r--r--railties/guides/code/getting_started/app/assets/javascripts/comments.js.coffee3
-rw-r--r--railties/guides/code/getting_started/app/assets/javascripts/home.js.coffee3
-rw-r--r--railties/guides/code/getting_started/app/assets/javascripts/posts.js.coffee3
-rw-r--r--railties/guides/code/getting_started/app/assets/stylesheets/application.css7
-rw-r--r--railties/guides/code/getting_started/app/assets/stylesheets/comments.css.scss3
-rw-r--r--railties/guides/code/getting_started/app/assets/stylesheets/home.css.scss3
-rw-r--r--railties/guides/code/getting_started/app/assets/stylesheets/posts.css.scss3
-rw-r--r--railties/guides/code/getting_started/app/assets/stylesheets/scaffolds.css.scss56
-rw-r--r--railties/guides/code/getting_started/app/controllers/application_controller.rb3
-rw-r--r--railties/guides/code/getting_started/app/controllers/comments_controller.rb16
-rw-r--r--railties/guides/code/getting_started/app/controllers/home_controller.rb5
-rw-r--r--railties/guides/code/getting_started/app/controllers/posts_controller.rb84
-rw-r--r--railties/guides/code/getting_started/app/helpers/application_helper.rb2
-rw-r--r--railties/guides/code/getting_started/app/helpers/comments_helper.rb2
-rw-r--r--railties/guides/code/getting_started/app/helpers/home_helper.rb2
-rw-r--r--railties/guides/code/getting_started/app/helpers/posts_helper.rb5
-rw-r--r--railties/guides/code/getting_started/app/mailers/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/app/models/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/app/models/comment.rb3
-rw-r--r--railties/guides/code/getting_started/app/models/post.rb11
-rw-r--r--railties/guides/code/getting_started/app/models/tag.rb3
-rw-r--r--railties/guides/code/getting_started/app/views/comments/_comment.html.erb15
-rw-r--r--railties/guides/code/getting_started/app/views/comments/_form.html.erb13
-rw-r--r--railties/guides/code/getting_started/app/views/home/index.html.erb2
-rw-r--r--railties/guides/code/getting_started/app/views/home/index.html.erb~2
-rw-r--r--railties/guides/code/getting_started/app/views/layouts/application.html.erb14
-rw-r--r--railties/guides/code/getting_started/app/views/posts/_form.html.erb32
-rw-r--r--railties/guides/code/getting_started/app/views/posts/edit.html.erb6
-rw-r--r--railties/guides/code/getting_started/app/views/posts/index.html.erb27
-rw-r--r--railties/guides/code/getting_started/app/views/posts/new.html.erb5
-rw-r--r--railties/guides/code/getting_started/app/views/posts/show.html.erb31
-rw-r--r--railties/guides/code/getting_started/app/views/tags/_form.html.erb12
-rw-r--r--railties/guides/code/getting_started/config.ru4
-rw-r--r--railties/guides/code/getting_started/config/application.rb48
-rw-r--r--railties/guides/code/getting_started/config/boot.rb6
-rw-r--r--railties/guides/code/getting_started/config/database.yml25
-rw-r--r--railties/guides/code/getting_started/config/environment.rb5
-rw-r--r--railties/guides/code/getting_started/config/environments/development.rb30
-rw-r--r--railties/guides/code/getting_started/config/environments/production.rb60
-rw-r--r--railties/guides/code/getting_started/config/environments/test.rb42
-rw-r--r--railties/guides/code/getting_started/config/initializers/backtrace_silencers.rb7
-rw-r--r--railties/guides/code/getting_started/config/initializers/inflections.rb10
-rw-r--r--railties/guides/code/getting_started/config/initializers/mime_types.rb5
-rw-r--r--railties/guides/code/getting_started/config/initializers/secret_token.rb7
-rw-r--r--railties/guides/code/getting_started/config/initializers/session_store.rb8
-rw-r--r--railties/guides/code/getting_started/config/initializers/wrap_parameters.rb14
-rw-r--r--railties/guides/code/getting_started/config/locales/en.yml5
-rw-r--r--railties/guides/code/getting_started/config/routes.rb64
-rw-r--r--railties/guides/code/getting_started/config/routes.rb~60
-rw-r--r--railties/guides/code/getting_started/db/migrate/20110901012504_create_posts.rb11
-rw-r--r--railties/guides/code/getting_started/db/migrate/20110901012815_create_comments.rb12
-rw-r--r--railties/guides/code/getting_started/db/migrate/20110901013701_create_tags.rb11
-rw-r--r--railties/guides/code/getting_started/db/schema.rb43
-rw-r--r--railties/guides/code/getting_started/db/seeds.rb7
-rw-r--r--railties/guides/code/getting_started/doc/README_FOR_APP2
-rw-r--r--railties/guides/code/getting_started/lib/assets/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/lib/tasks/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/public/404.html26
-rw-r--r--railties/guides/code/getting_started/public/422.html26
-rw-r--r--railties/guides/code/getting_started/public/500.html26
-rw-r--r--railties/guides/code/getting_started/public/favicon.ico0
-rw-r--r--railties/guides/code/getting_started/public/robots.txt5
-rwxr-xr-xrailties/guides/code/getting_started/script/rails6
-rw-r--r--railties/guides/code/getting_started/test/fixtures/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/test/fixtures/comments.yml11
-rw-r--r--railties/guides/code/getting_started/test/fixtures/posts.yml11
-rw-r--r--railties/guides/code/getting_started/test/fixtures/tags.yml9
-rw-r--r--railties/guides/code/getting_started/test/functional/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/test/functional/comments_controller_test.rb7
-rw-r--r--railties/guides/code/getting_started/test/functional/home_controller_test.rb9
-rw-r--r--railties/guides/code/getting_started/test/functional/posts_controller_test.rb49
-rw-r--r--railties/guides/code/getting_started/test/integration/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/test/performance/browsing_test.rb12
-rw-r--r--railties/guides/code/getting_started/test/test_helper.rb13
-rw-r--r--railties/guides/code/getting_started/test/unit/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/test/unit/comment_test.rb7
-rw-r--r--railties/guides/code/getting_started/test/unit/helpers/comments_helper_test.rb4
-rw-r--r--railties/guides/code/getting_started/test/unit/helpers/home_helper_test.rb4
-rw-r--r--railties/guides/code/getting_started/test/unit/helpers/posts_helper_test.rb4
-rw-r--r--railties/guides/code/getting_started/test/unit/post_test.rb7
-rw-r--r--railties/guides/code/getting_started/test/unit/tag_test.rb7
-rw-r--r--railties/guides/code/getting_started/vendor/assets/stylesheets/.gitkeep0
-rw-r--r--railties/guides/code/getting_started/vendor/plugins/.gitkeep0
-rw-r--r--railties/guides/source/3_1_release_notes.textile10
-rw-r--r--railties/guides/source/action_controller_overview.textile4
-rw-r--r--railties/guides/source/action_mailer_basics.textile9
-rw-r--r--railties/guides/source/action_view_overview.textile77
-rw-r--r--railties/guides/source/active_model_basics.textile2
-rw-r--r--railties/guides/source/active_record_querying.textile79
-rw-r--r--railties/guides/source/active_support_core_extensions.textile2
-rw-r--r--railties/guides/source/asset_pipeline.textile332
-rw-r--r--railties/guides/source/configuring.textile14
-rw-r--r--railties/guides/source/contributing_to_ruby_on_rails.textile2
-rw-r--r--railties/guides/source/generators.textile9
-rw-r--r--railties/guides/source/getting_started.textile21
-rw-r--r--railties/guides/source/index.html.erb8
-rw-r--r--railties/guides/source/layout.html.erb8
-rw-r--r--railties/guides/source/layouts_and_rendering.textile11
-rw-r--r--railties/guides/source/performance_testing.textile4
-rw-r--r--railties/guides/source/plugins.textile22
-rw-r--r--railties/guides/source/rails_application_templates.textile12
-rw-r--r--railties/guides/source/testing.textile2
-rw-r--r--railties/lib/rails/application/configuration.rb14
-rw-r--r--railties/lib/rails/application/route_inspector.rb42
-rw-r--r--railties/lib/rails/generators/actions.rb33
-rw-r--r--railties/lib/rails/generators/app_base.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/500.html1
-rw-r--r--railties/lib/rails/generators/test_case.rb6
-rw-r--r--railties/lib/rails/generators/test_unit/model/templates/fixtures.yml2
-rw-r--r--railties/lib/rails/tasks/routes.rake34
-rw-r--r--railties/test/application/asset_debugging_test.rb27
-rw-r--r--railties/test/application/assets_test.rb189
-rw-r--r--railties/test/application/rake_test.rb152
-rw-r--r--railties/test/application/route_inspect_test.rb97
-rw-r--r--railties/test/application/routing_test.rb2
-rw-r--r--railties/test/generators/actions_test.rb16
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb6
-rw-r--r--railties/test/generators/shared_generator_tests.rb4
-rw-r--r--railties/test/railties/engine_test.rb2
-rw-r--r--railties/test/railties/mounted_engine_test.rb43
-rw-r--r--railties/test/railties/shared_tests.rb10
231 files changed, 4025 insertions, 871 deletions
diff --git a/Gemfile b/Gemfile
index f0880926a3..54e171b0db 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,10 +6,11 @@ if ENV['AREL']
gem "arel", :path => ENV['AREL']
end
+gem "bcrypt-ruby", "~> 3.0.0"
gem "jquery-rails"
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
-gem "uglifier", ">= 1.0.0", :require => false
+gem "uglifier", ">= 1.0.3", :require => false
# Temp fix until rake 0.9.3 is out
if RUBY_VERSION >= "1.9.3"
diff --git a/README.rdoc b/README.rdoc
index 9f017cd05f..0def4e5d12 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -61,13 +61,14 @@ can read more about Action Pack in its {README}[link:/rails/rails/blob/master/ac
* The {Ruby on Rails Guides}[http://guides.rubyonrails.org].
* The {API Documentation}[http://api.rubyonrails.org].
-
-== Contributing http://travis-ci.org/rails/rails.png
+== Contributing
We encourage you to contribute to Ruby on Rails! Please check out the {Contributing to Rails
guide}[http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how
to proceed. {Join us}[http://contributors.rubyonrails.org]!
+== Travis Build Status {<img src="https://secure.travis-ci.org/rails/rails.png"/>}[http://travis-ci.org/rails/rails]
+
== License
Ruby on Rails is released under the MIT license.
diff --git a/Rakefile b/Rakefile
index f171949961..a47d415abc 100755
--- a/Rakefile
+++ b/Rakefile
@@ -78,7 +78,8 @@ RDoc::Task.new do |rdoc|
rdoc_main.gsub!(%r{link:/rails/rails/blob/master/(\w+)/README\.rdoc}, "link:files/\\1/README_rdoc.html")
# Remove Travis build status image from API pages. Only GitHub README page gets this image
- rdoc_main.gsub!("http://travis-ci.org/rails/rails.png", "")
+ # https build image is used to avoid GitHub caching: http://about.travis-ci.org/docs/user/status-images
+ rdoc_main.gsub!(%r{^== Travis.*}, '')
File.open(RDOC_MAIN, 'w') do |f|
f.write(rdoc_main)
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 6b654e149e..b757df6f22 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,23 @@
*Rails 3.2.0 (unreleased)*
+* Changed log level of warning for missing CSRF token from :debug to :warn. [Mike Dillon]
+
+* content_tag_for and div_for can now take the collection of records. It will also yield the record as the first argument if you set a receiving argument in your block [Prem Sichanugrist]
+
+ So instead of having to do this:
+
+ @items.each do |item|
+ content_tag_for(:li, item) do
+ Title: <%= item.title %>
+ end
+ end
+
+ You can now do this:
+
+ content_tag_for(:li, @items) do |item|
+ Title: <%= item.title %>
+ end
+
* send_file now guess the mime type [Esad Hajdarevic]
* Mime type entries for PDF, ZIP and other formats were added [Esad Hajdarevic]
@@ -28,7 +46,21 @@
for your test you need to do it before the cookie jar is created.
-*Rails 3.1.0 (unreleased)*
+*Rails 3.1.1 (unreleased)*
+
+* Fixed the behavior of asset pipeline when config.assets.digest and config.assets.compile are false and requested asset isn't precompiled.
+ Before the requested asset were compiled anyway ignoring that the config.assets.compile flag is false. [Guillermo Iguaran]
+
+* CookieJar is now Enumerable. Fixes #2795
+
+* Fixed AssetNotPrecompiled error raised when rake assets:precompile is compiling certain .erb files. See GH #2763 #2765 #2805 [Guillermo Iguaran]
+
+* Manifest is correctly placed in assets path when default assets prefix is changed. Fixes #2776 [Guillermo Iguaran]
+
+* Fixed stylesheet_link_tag and javascript_include_tag to respect additional options passed by the users when debug is on. [Guillermo Iguaran]
+
+
+*Rails 3.1.0 (August 30, 2011)*
* Param values are `paramified` in controller tests. [David Chelimsky]
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 59d5bbc5ed..65b364f872 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
s.add_dependency('rack', '~> 1.3.2')
s.add_dependency('rack-test', '~> 0.6.1')
s.add_dependency('rack-mount', '~> 0.8.2')
- s.add_dependency('sprockets', '~> 2.0.0.beta.15')
+ s.add_dependency('sprockets', '~> 2.0.0')
s.add_dependency('erubis', '~> 2.7.0')
s.add_development_dependency('tzinfo', '~> 0.3.29')
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index 938a6ae81c..49cf70ec21 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -88,7 +88,7 @@ module ActionController #:nodoc:
end
def method_missing(method, *arguments, &block)
- return if @controller.nil?
+ return unless @controller
@controller.__send__(method, *arguments, &block)
end
end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 2df0e9422c..bd515bba82 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -7,9 +7,12 @@ module ActionController
# by default.
#
# In addition to using the standard template helpers provided, creating custom helpers to
- # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
- # include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
- # include <tt>MyHelper</tt>.
+ # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
+ # will include all helpers.
+ #
+ # In previous versions of \Rails the controller will include a helper whose
+ # name matches that of the controller, e.g., <tt>MyController</tt> will automatically
+ # include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
#
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
# controller which inherits from it.
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 7420a5e7e9..264806cd36 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -145,7 +145,7 @@ module ActionController
end
def encode_credentials(user_name, password)
- "Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}"
+ "Basic #{ActiveSupport::Base64.encode64s("#{user_name}:#{password}")}"
end
def authentication_request(controller, realm)
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 4f311a1cf5..f2dfb3833b 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -57,7 +57,7 @@ module ActionController
# When using <tt>redirect_to :back</tt>, if there is no referrer, RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
- raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
+ raise ActionControllerError.new("Cannot redirect to nil!") unless options
raise AbstractController::DoubleRenderError if response_body
self.status = _extract_redirect_to_status(options, response_status)
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 4d016271ea..bc22e39efb 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -74,7 +74,7 @@ module ActionController #:nodoc:
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
unless verified_request?
- logger.debug "WARNING: Can't verify CSRF token authenticity" if logger
+ logger.warn "WARNING: Can't verify CSRF token authenticity" if logger
handle_unverified_request
end
end
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index c11d676c5e..2036442cfe 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -67,7 +67,7 @@ module ActionController
# This can be overwritten to customize the default generated string representation if desired.
# If you need to read back a key from a dom_id in order to query for the underlying database record,
# you should write a helper like 'person_record_from_dom_id' that will extract the key either based
- # on the default implementation (which just joins all key attributes with '-') or on your own
+ # on the default implementation (which just joins all key attributes with '_') or on your own
# overwritten version of the method. By default, this implementation passes the key string through a
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
# make sure yourself that your dom ids are valid, in case you overwrite this method.
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 40332da321..a83fa74795 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -79,10 +79,10 @@ module ActionController
"expecting <?> but rendering with <?>",
options, rendered.keys.join(', '))
assert_block(msg) do
- if options.nil?
- @templates.blank?
- else
+ if options
rendered.any? { |t,num| t.match(options) }
+ else
+ @templates.blank?
end
end
when Hash
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 1c312f2587..8c4615c0c1 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -85,6 +85,7 @@ module ActionDispatch
class CookieOverflow < StandardError; end
class CookieJar #:nodoc:
+ include Enumerable
# This regular expression is used to split the levels of a domain.
# The top level domain can be any string without a period or
@@ -124,6 +125,10 @@ module ActionDispatch
alias :closed? :closed
def close!; @closed = true end
+ def each(&block)
+ @cookies.each(&block)
+ end
+
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
def [](name)
@cookies[name.to_s]
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index a70d814749..6bcf099d2c 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -59,7 +59,10 @@ module ActionDispatch
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
- raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
+ raise ActionDispatch::Session::SessionRestoreError,
+ "Session contains objects whose class definition isn't available.\n" +
+ "Remember to require the classes for all objects kept in the session.\n" +
+ "(Original exception: #{const_error.message} [#{const_error.class}])\n"
end
retry
else
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index a5c1501f61..4d65173f61 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -452,7 +452,9 @@ module ActionDispatch
prefix_options = options.slice(*_route.segment_keys)
# we must actually delete prefix segment keys to avoid passing them to next url_for
_route.segment_keys.each { |k| options.delete(k) }
- _routes.url_helpers.send("#{name}_path", prefix_options)
+ prefix = _routes.url_helpers.send("#{name}_path", prefix_options)
+ prefix = '' if prefix == '/'
+ prefix
end
end
end
@@ -1036,12 +1038,12 @@ module ActionDispatch
#
# This generates the following comments routes:
#
- # GET /photos/:id/comments/new
- # POST /photos/:id/comments
- # GET /photos/:id/comments/:id
- # GET /photos/:id/comments/:id/edit
- # PUT /photos/:id/comments/:id
- # DELETE /photos/:id/comments/:id
+ # GET /photos/:photo_id/comments/new
+ # POST /photos/:photo_id/comments
+ # GET /photos/:photo_id/comments/:id
+ # GET /photos/:photo_id/comments/:id/edit
+ # PUT /photos/:photo_id/comments/:id
+ # DELETE /photos/:photo_id/comments/:id
#
# === Options
# Takes same options as <tt>Base#match</tt> as well as:
@@ -1436,7 +1438,7 @@ module ActionDispatch
name_prefix = @scope[:as]
if parent_resource
- return nil if as.nil? && action.nil?
+ return nil unless as || action
collection_name = parent_resource.collection_name
member_name = parent_resource.member_name
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index 5fa91d1a76..b4555f4f59 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -415,9 +415,9 @@ module ActionDispatch
assert !deliveries.empty?, "No e-mail in delivery list"
for delivery in deliveries
- for part in delivery.parts
+ for part in (delivery.parts.empty? ? [delivery] : delivery.parts)
if part["Content-Type"].to_s =~ /^text\/html\W/
- root = HTML::Document.new(part.body).root
+ root = HTML::Document.new(part.body.to_s).root
assert_select root, ":root", &block
end
end
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb
index aae8377f8a..73f4f8ee5f 100644
--- a/actionpack/lib/action_view/asset_paths.rb
+++ b/actionpack/lib/action_view/asset_paths.rb
@@ -69,7 +69,7 @@ module ActionView
host = "#{compute_protocol(protocol)}#{host}"
end
end
- host.nil? ? source : "#{host}#{source}"
+ host ? "#{host}#{source}" : source
end
def compute_protocol(protocol)
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 43d67f2032..36c49d9c91 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -116,7 +116,7 @@ module ActionView #:nodoc:
# xml.language "en-us"
# xml.ttl "40"
#
- # for item in @recent_items
+ # @recent_items.each do |item|
# xml.item do
# xml.title(item_title(item))
# xml.description(item_description(item)) if item_description(item)
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 62f95379cd..8abd85c3a3 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -134,9 +134,9 @@ module ActionView
# WARNING: content_for is ignored in caches. So you shouldn't use it
# for elements that will be fragment cached.
def content_for(name, content = nil, &block)
- content = capture(&block) if block_given?
- if content
- @view_flow.append(name, content)
+ if content || block_given?
+ content = capture(&block) if block_given?
+ @view_flow.append(name, content) if content
nil
else
@view_flow.get(name)
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 1ceb53fe9c..13b9dc8553 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -656,7 +656,7 @@ module ActionView
if token == false || !protect_against_forgery?
''
else
- token = form_authenticity_token if token.nil?
+ token ||= form_authenticity_token
tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
end
end
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index 142a25f118..b351302d01 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -17,6 +17,19 @@ module ActionView
#
# <div id="person_123" class="person foo"> Joe Bloggs </div>
#
+ # You can also pass an array of Active Record objects, which will then
+ # get iterated over and yield each record as an argument for the block.
+ # For example:
+ #
+ # <%= div_for(@people, :class => "foo") do |person| %>
+ # <%= person.name %>
+ # <% end %>
+ #
+ # produces:
+ #
+ # <div id="person_123" class="person foo"> Joe Bloggs </div>
+ # <div id="person_124" class="person foo"> Jane Bloggs </div>
+ #
def div_for(record, *args, &block)
content_tag_for(:div, record, *args, &block)
end
@@ -42,6 +55,21 @@ module ActionView
#
# <tr id="foo_person_123" class="person">...
#
+ # You can also pass an array of objects which this method will loop through
+ # and yield the current object to the supplied block, reducing the need for
+ # having to iterate through the object (using <tt>each</tt>) beforehand.
+ # For example (assuming @people is an array of Person objects):
+ #
+ # <%= content_tag_for(:tr, @people) do |person| %>
+ # <td><%= person.first_name %></td>
+ # <td><%= person.last_name %></td>
+ # <% end %>
+ #
+ # produces:
+ #
+ # <tr id="person_123" class="person">...</tr>
+ # <tr id="person_124" class="person">...</tr>
+ #
# content_tag_for also accepts a hash of options, which will be converted to
# additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
# with the default class name for your object. For example:
@@ -52,12 +80,30 @@ module ActionView
#
# <li id="person_123" class="person bar">...
#
- def content_tag_for(tag_name, record, prefix = nil, options = nil, &block)
- options, prefix = prefix, nil if prefix.is_a?(Hash)
- options ||= {}
- options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) })
- content_tag(tag_name, options, &block)
+ def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block)
+ if single_or_multiple_records.respond_to?(:to_ary)
+ single_or_multiple_records.to_ary.map do |single_record|
+ capture { content_tag_for_single_record(tag_name, single_record, prefix, options, &block) }
+ end.join("\n").html_safe
+ else
+ content_tag_for_single_record(tag_name, single_or_multiple_records, prefix, options, &block)
+ end
end
+
+ private
+
+ # Called by <tt>content_tag_for</tt> internally to render a content tag
+ # for each record.
+ def content_tag_for_single_record(tag_name, record, prefix, options, &block)
+ options, prefix = prefix, nil if prefix.is_a?(Hash)
+ options ||= {}
+ options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) })
+ if block.arity == 0
+ content_tag(tag_name, capture(&block), options)
+ else
+ content_tag(tag_name, capture(record, &block), options)
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 4dbb0135f6..acd5e46e33 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -569,6 +569,12 @@ module ActionView
#
# current_page?(:controller => 'library', :action => 'checkout')
# # => false
+ #
+ # Let's say we're in the <tt>/products</tt> action with method POST in case of invalid product.
+ #
+ # current_page?(:controller => 'product', :action => 'index')
+ # # => false
+ #
def current_page?(options)
unless request
raise "You cannot use helpers that need to determine the current " \
@@ -576,6 +582,8 @@ module ActionView
"in a #request method"
end
+ return false unless request.get?
+
url_string = url_for(options)
# We ignore any extra parameters in the request_uri if the
@@ -596,9 +604,7 @@ module ActionView
private
def convert_options_to_data_attributes(options, html_options)
- if html_options.nil?
- link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
- else
+ if html_options
html_options = html_options.stringify_keys
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
@@ -611,6 +617,8 @@ module ActionView
add_method_to_attributes!(html_options, method) if method
html_options
+ else
+ link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
end
end
diff --git a/actionpack/lib/sprockets/assets.rake b/actionpack/lib/sprockets/assets.rake
index 7594ee4296..893ecf58fe 100644
--- a/actionpack/lib/sprockets/assets.rake
+++ b/actionpack/lib/sprockets/assets.rake
@@ -13,35 +13,39 @@ namespace :assets do
# Ensure that action view is loaded and the appropriate sprockets hooks get executed
ActionView::Base
- # Always perform caching so that asset_path appends the timestamps to file references.
- Rails.application.config.action_controller.perform_caching = true
+ # Always compile files
+ Rails.application.config.assets.compile = true
config = Rails.application.config
env = Rails.application.assets
- target = Rails.root.join("public#{config.assets.prefix}")
-
- if env.respond_to?(:each_logical_path)
- config.assets.precompile.each do |path|
- env.each_logical_path do |logical_path|
- if path.is_a?(Regexp)
- next unless path.match(logical_path)
- else
- next unless File.fnmatch(path.to_s, logical_path)
- end
-
- if asset = env.find_asset(logical_path)
- filename = target.join(asset.digest_path)
- mkdir_p filename.dirname
- asset.write_to(filename)
- asset.write_to("#{filename}.gz") if filename.to_s =~ /\.(css|js)$/
- end
+ target = Pathname.new(File.join(Rails.public_path, config.assets.prefix))
+ manifest = {}
+ manifest_path = config.assets.manifest || target
+
+ config.assets.precompile.each do |path|
+ env.each_logical_path do |logical_path|
+ if path.is_a?(Regexp)
+ next unless path.match(logical_path)
+ elsif path.is_a?(Proc)
+ next unless path.call(logical_path)
+ else
+ next unless File.fnmatch(path.to_s, logical_path)
+ end
+
+ if asset = env.find_asset(logical_path)
+ asset_path = config.assets.digest ? asset.digest_path : logical_path
+ manifest[logical_path] = asset_path
+ filename = target.join(asset_path)
+
+ mkdir_p filename.dirname
+ asset.write_to(filename)
+ asset.write_to("#{filename}.gz") if filename.to_s =~ /\.(css|js)$/
end
end
- else
- # TODO: Remove this once we're depending on sprockets beta 15
- assets = config.assets.precompile.dup
- assets << {:to => target}
- env.precompile(*assets)
+ end
+
+ File.open("#{manifest_path}/manifest.yml", 'w') do |f|
+ YAML.dump(manifest, f)
end
end
end
diff --git a/actionpack/lib/sprockets/compressors.rb b/actionpack/lib/sprockets/compressors.rb
index 6544953df4..351eff1085 100644
--- a/actionpack/lib/sprockets/compressors.rb
+++ b/actionpack/lib/sprockets/compressors.rb
@@ -1,21 +1,37 @@
module Sprockets
- class NullCompressor
+ # An asset compressor which does nothing.
+ #
+ # This compressor simply returns the asset as-is, without any compression
+ # whatsoever. It is useful in development mode, when compression isn't
+ # needed but using the same asset pipeline as production is desired.
+ class NullCompressor #:nodoc:
def compress(content)
content
end
end
- class LazyCompressor
+ # An asset compressor which only initializes the underlying compression
+ # engine when needed.
+ #
+ # This postpones the initialization of the compressor until
+ # <code>#compress</code> is called the first time.
+ class LazyCompressor #:nodoc:
+ # Initializes a new LazyCompressor.
+ #
+ # The block should return a compressor when called, i.e. an object
+ # which responds to <code>#compress</code>.
def initialize(&block)
@block = block
end
- def compressor
- @compressor ||= @block.call || NullCompressor.new
- end
-
def compress(content)
compressor.compress(content)
end
+
+ private
+
+ def compressor
+ @compressor ||= (@block.call || NullCompressor.new)
+ end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb
index 7a2bf8bef6..3987e6e17f 100644
--- a/actionpack/lib/sprockets/helpers/rails_helper.rb
+++ b/actionpack/lib/sprockets/helpers/rails_helper.rb
@@ -14,6 +14,9 @@ module Sprockets
paths = RailsHelper::AssetPaths.new(config, controller)
paths.asset_environment = asset_environment
paths.asset_prefix = asset_prefix
+ paths.asset_digests = asset_digests
+ paths.compile_assets = compile_assets?
+ paths.digest_assets = digest_assets?
paths
end
end
@@ -58,8 +61,9 @@ module Sprockets
private
def debug_assets?
- config = Rails.application.config.assets
- config.allow_debugging && (config.debug || params[:debug_assets])
+ compile_assets? && (Rails.application.config.assets.debug || params[:debug_assets])
+ rescue NoMethodError
+ false
end
# Override to specify an alternative prefix for asset path generation.
@@ -72,6 +76,18 @@ module Sprockets
Rails.application.config.assets.prefix
end
+ def asset_digests
+ Rails.application.config.assets.digests
+ end
+
+ def compile_assets?
+ Rails.application.config.assets.compile
+ end
+
+ def digest_assets?
+ Rails.application.config.assets.digest
+ end
+
# Override to specify an alternative asset environment for asset
# path generation. The environment should already have been mounted
# at the prefix returned by +asset_prefix+.
@@ -80,7 +96,9 @@ module Sprockets
end
class AssetPaths < ::ActionView::AssetPaths #:nodoc:
- attr_accessor :asset_environment, :asset_prefix
+ attr_accessor :asset_environment, :asset_prefix, :asset_digests, :compile_assets, :digest_assets
+
+ class AssetNotPrecompiledError < StandardError; end
def compute_public_path(source, dir, ext=nil, include_host=true, protocol=nil)
super(source, asset_prefix, ext, include_host, protocol)
@@ -99,18 +117,25 @@ module Sprockets
end
def digest_for(logical_path)
- if asset = asset_environment[logical_path]
- return asset.digest_path
+ if asset_digests && (digest = asset_digests[logical_path])
+ return digest
end
- logical_path
+ if compile_assets
+ if digest_assets && asset = asset_environment[logical_path]
+ return asset.digest_path
+ end
+ return logical_path
+ else
+ raise AssetNotPrecompiledError.new("#{logical_path} isn't precompiled")
+ end
end
def rewrite_asset_path(source, dir)
if source[0] == ?/
source
else
- source = digest_for(source) if performing_caching?
+ source = digest_for(source)
source = File.join(dir, source)
source = "/#{source}" unless source =~ /^\//
source
@@ -124,16 +149,6 @@ module Sprockets
source
end
end
-
- def performing_caching?
- # When included in Sprockets::Context, we need to ask the
- # top-level config as the controller is not available.
- if config.action_controller.present?
- config.action_controller.perform_caching
- else
- config.perform_caching
- end
- end
end
end
end
diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb
index c21bf57935..dc991636a1 100644
--- a/actionpack/lib/sprockets/railtie.rb
+++ b/actionpack/lib/sprockets/railtie.rb
@@ -26,6 +26,16 @@ module Sprockets
end
end
+ if config.assets.manifest
+ path = File.join(config.assets.manifest, "manifest.yml")
+ else
+ path = File.join(Rails.public_path, config.assets.prefix, "manifest.yml")
+ end
+
+ if File.exist?(path)
+ config.assets.digests = YAML.load_file(path)
+ end
+
ActiveSupport.on_load(:action_view) do
include ::Sprockets::Helpers::RailsHelper
@@ -61,7 +71,7 @@ module Sprockets
mount app.assets => config.assets.prefix
end
- if config.action_controller.perform_caching
+ if config.assets.digest
app.assets = app.assets.index
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index aa7a01f6c9..24d071df39 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -142,11 +142,7 @@ class RoutedRackApp
end
class BasicController
- attr_accessor :request, :params
-
- def initialize
- @params = {}
- end
+ attr_accessor :request
def config
@config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 878484eb57..5eef8a32d7 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -20,6 +20,15 @@ class AssertSelectTest < ActionController::TestCase
end
end
+ class AssertMultipartSelectMailer < ActionMailer::Base
+ def test(options)
+ mail :subject => "Test e-mail", :from => "test@test.host", :to => "test <test@test.host>" do |format|
+ format.text { render :text => options[:text] }
+ format.html { render :text => options[:html] }
+ end
+ end
+ end
+
class AssertSelectController < ActionController::Base
def response_with=(content)
@content = content
@@ -313,6 +322,16 @@ EOF
end
end
+ def test_assert_select_email_multipart
+ AssertMultipartSelectMailer.test(:html => "<div><p>foo</p><p>bar</p></div>", :text => 'foo bar').deliver
+ assert_select_email do
+ assert_select "div:root" do
+ assert_select "p:first-child", "foo"
+ assert_select "p:last-child", "bar"
+ end
+ end
+ end
+
protected
def render_html(html)
@controller.response_with = html
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb
index bd3e13e6fa..364e96d4f6 100644
--- a/actionpack/test/controller/http_basic_authentication_test.rb
+++ b/actionpack/test/controller/http_basic_authentication_test.rb
@@ -85,6 +85,14 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
end
end
+ def test_encode_credentials_has_no_newline
+ username = 'laskjdfhalksdjfhalkjdsfhalksdjfhklsdjhalksdjfhalksdjfhlakdsjfh'
+ password = 'kjfhueyt9485osdfasdkljfh4lkjhakldjfhalkdsjf'
+ result = ActionController::HttpAuthentication::Basic.encode_credentials(
+ username, password)
+ assert_no_match(/\n/, result)
+ end
+
test "authentication request without credential" do
get :display
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 6bcd606bf4..c46755417f 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -410,7 +410,7 @@ class TestController < ActionController::Base
end
def render_with_explicit_escaped_template
- render :template => "test/hello_w*rld"
+ render :template => "test/hello,world"
end
def render_with_explicit_string_template
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index d94db7f5fb..fd5a41a0bb 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'digest/sha1'
require 'active_support/core_ext/string/strip'
+require "active_support/log_subscriber/test_helper"
# common controller actions
module RequestForgeryProtectionActions
@@ -157,6 +158,21 @@ module RequestForgeryProtectionTests
assert_not_blocked { put :index }
end
+ def test_should_warn_on_missing_csrf_token
+ old_logger = ActionController::Base.logger
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ ActionController::Base.logger = logger
+
+ begin
+ assert_blocked { post :index }
+
+ assert_equal 1, logger.logged(:warn).size
+ assert_match(/CSRF token authenticity/, logger.logged(:warn).last)
+ ensure
+ ActionController::Base.logger = old_logger
+ end
+ end
+
def assert_blocked
session[:something_like_user_id] = 1
yield
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 3b1b5fc3ec..b9cd15708b 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -532,7 +532,7 @@ class ResourcesTest < ActionController::TestCase
routes.each do |route|
routes.each do |r|
next if route === r # skip the comparison instance
- assert distinct_routes?(route, r), "Duplicate Route: #{route}"
+ assert_not_equal route.conditions, r.conditions
end
end
end
@@ -1351,13 +1351,4 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(expected_options, path)
end
end
-
- def distinct_routes? (r1, r2)
- if r1.conditions == r2.conditions and r1.constraints == r2.constraints then
- if r1.segments.collect(&:to_s) == r2.segments.collect(&:to_s) then
- return false
- end
- end
- true
- end
end
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index fb67ecb07d..49da448001 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -148,6 +148,22 @@ class CookiesTest < ActionController::TestCase
@request.host = "www.nextangle.com"
end
+ def test_each
+ request.cookie_jar['foo'] = :bar
+ list = []
+ request.cookie_jar.each do |k,v|
+ list << [k, v]
+ end
+
+ assert_equal [['foo', :bar]], list
+ end
+
+ def test_enumerable
+ request.cookie_jar['foo'] = :bar
+ actual = request.cookie_jar.map { |k,v| [k.to_s, v.to_s] }
+ assert_equal [['foo', 'bar']], actual
+ end
+
def test_key_methods
assert !request.cookie_jar.key?(:foo)
assert !request.cookie_jar.has_key?("foo")
diff --git a/actionpack/test/fixtures/test/hello_w*rld.erb b/actionpack/test/fixtures/test/hello,world.erb
index bc8fa5e0ca..bc8fa5e0ca 100644
--- a/actionpack/test/fixtures/test/hello_w*rld.erb
+++ b/actionpack/test/fixtures/test/hello,world.erb
diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb
index a9157e711c..13e2d5b595 100644
--- a/actionpack/test/template/capture_helper_test.rb
+++ b/actionpack/test/template/capture_helper_test.rb
@@ -46,6 +46,42 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal "bar", content_for(:bar)
end
+ def test_content_for_with_multiple_calls
+ assert ! content_for?(:title)
+ content_for :title, 'foo'
+ content_for :title, 'bar'
+ assert_equal 'foobar', content_for(:title)
+ end
+
+ def test_content_for_with_block
+ assert ! content_for?(:title)
+ content_for :title do
+ output_buffer << 'foo'
+ output_buffer << 'bar'
+ nil
+ end
+ assert_equal 'foobar', content_for(:title)
+ end
+
+ def test_content_for_with_whitespace_block
+ assert ! content_for?(:title)
+ content_for :title, 'foo'
+ content_for :title do
+ output_buffer << " \n "
+ nil
+ end
+ content_for :title, 'bar'
+ assert_equal 'foobar', content_for(:title)
+ end
+
+ def test_content_for_returns_nil_when_writing
+ assert ! content_for?(:title)
+ assert_equal nil, content_for(:title, 'foo')
+ assert_equal nil, content_for(:title) { output_buffer << 'bar'; nil }
+ assert_equal nil, content_for(:title) { output_buffer << " \n "; nil }
+ assert_equal 'foobar', content_for(:title)
+ end
+
def test_content_for_question_mark
assert ! content_for?(:title)
content_for :title, 'title'
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index f898c22e1e..e36d032f6c 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -698,8 +698,7 @@ class FormHelperTest < ActionView::TestCase
expected = whole_form("/posts/44", "edit_post_44" , "edit_post", :method => "put") do
"<input name='post[title]' size='30' type='text' id='post_title' value='And his name will be forty and four.' />" +
- "<input name='commit' type='submit' value='Edit post' />" +
- "</form>"
+ "<input name='commit' type='submit' value='Edit post' />"
end
assert_dom_equal expected, output_buffer
diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb
index 1ba14e8bc9..7f23629e05 100644
--- a/actionpack/test/template/record_tag_helper_test.rb
+++ b/actionpack/test/template/record_tag_helper_test.rb
@@ -4,11 +4,20 @@ require 'controller/fake_models'
class Post
extend ActiveModel::Naming
include ActiveModel::Conversion
+ attr_writer :id, :body
+
+ def initialize
+ @id = nil
+ @body = nil
+ super
+ end
+
def id
- 45
+ @id || 45
end
+
def body
- super || "What a wonderful world!"
+ super || @body || "What a wonderful world!"
end
end
@@ -58,4 +67,32 @@ class RecordTagHelperTest < ActionView::TestCase
actual = div_for(@post, :class => "bar") { concat @post.body }
assert_dom_equal expected, actual
end
+
+ def test_content_tag_for_collection
+ post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
+ post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
+ expected = %(<li class="post" id="post_101">Hello!</li>\n<li class="post" id="post_102">World!</li>)
+ actual = content_tag_for(:li, [post_1, post_2]) { |post| concat post.body }
+ assert_dom_equal expected, actual
+ end
+
+ def test_div_for_collection
+ post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
+ post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
+ expected = %(<div class="post" id="post_101">Hello!</div>\n<div class="post" id="post_102">World!</div>)
+ actual = div_for([post_1, post_2]) { |post| concat post.body }
+ assert_dom_equal expected, actual
+ end
+
+ def test_content_tag_for_single_record_is_html_safe
+ result = div_for(@post, :class => "bar") { concat @post.body }
+ assert result.html_safe?
+ end
+
+ def test_content_tag_for_collection_is_html_safe
+ post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
+ post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
+ result = content_tag_for(:li, [post_1, post_2]) { |post| concat post.body }
+ assert result.html_safe?
+ end
end
diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb
index 6c1f97a44a..ae4cb1f0aa 100644
--- a/actionpack/test/template/sprockets_helper_test.rb
+++ b/actionpack/test/template/sprockets_helper_test.rb
@@ -30,6 +30,8 @@ class SprocketsHelperTest < ActionView::TestCase
@config = config
@config.action_controller ||= ActiveSupport::InheritableOptions.new
@config.perform_caching = true
+ @config.assets.digest = true
+ @config.assets.compile = true
end
def url_for(*args)
@@ -157,7 +159,7 @@ class SprocketsHelperTest < ActionView::TestCase
assert_match %r{<script src="/assets/xmlhr-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/application-[0-9a-f]+.js\?body=1" type="text/javascript"></script>},
javascript_include_tag(:application, :debug => true)
- @config.assets.allow_debugging = true
+ @config.assets.compile = true
@config.assets.debug = true
assert_match %r{<script src="/assets/xmlhr-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/application-[0-9a-f]+.js\?body=1" type="text/javascript"></script>},
javascript_include_tag(:application)
@@ -198,7 +200,7 @@ class SprocketsHelperTest < ActionView::TestCase
assert_match %r{<link href="/assets/style-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />\n<link href="/assets/application-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />},
stylesheet_link_tag(:application, :debug => true)
- @config.assets.allow_debugging = true
+ @config.assets.compile = true
@config.assets.debug = true
assert_match %r{<link href="/assets/style-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />\n<link href="/assets/application-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />},
stylesheet_link_tag(:application)
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 78245c1f95..dbac2e1fc0 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -304,8 +304,8 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_equal "Showing", link_to_if(false, "Showing", url_hash)
end
- def request_for_url(url)
- env = Rack::MockRequest.env_for("http://www.example.com#{url}")
+ def request_for_url(url, opts = {})
+ env = Rack::MockRequest.env_for("http://www.example.com#{url}", opts)
ActionDispatch::Request.new(env)
end
@@ -329,6 +329,12 @@ class UrlHelperTest < ActiveSupport::TestCase
assert current_page?("http://www.example.com/?order=desc&page=1")
end
+ def test_current_page_with_not_get_verb
+ @request = request_for_url("/events", :method => :post)
+
+ assert !current_page?('/events')
+ end
+
def test_link_unless_current
@request = request_for_url("/")
diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec
index e5075485bb..260ad01b65 100644
--- a/activemodel/activemodel.gemspec
+++ b/activemodel/activemodel.gemspec
@@ -19,5 +19,4 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', version)
s.add_dependency('builder', '~> 3.0.0')
s.add_dependency('i18n', '~> 0.6')
- s.add_dependency('bcrypt-ruby', '~> 3.0.0')
end
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index e3e71525fa..166cccf161 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -29,7 +29,7 @@ module ActiveModel
#
# include ActiveModel::Dirty
#
- # define_attribute_methods = [:name]
+ # define_attribute_methods [:name]
#
# def name
# @name
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 843c0c3cb5..d91e4a2b6a 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -88,6 +88,7 @@ module ActiveModel
def include?(error)
(v = messages[error]) && v.any?
end
+ alias :has_key? :include?
# Get messages for +key+
def get(key)
@@ -179,6 +180,7 @@ module ActiveModel
all? { |k, v| v && v.empty? }
end
alias_method :blank?, :empty?
+
# Returns an xml formatted representation of the Errors hash.
#
# p.errors.add(:name, "can't be blank")
@@ -253,20 +255,22 @@ module ActiveModel
# company.errors.full_messages # =>
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
def full_messages
- map { |attribute, message|
- if attribute == :base
- message
- else
- attr_name = attribute.to_s.gsub('.', '_').humanize
- attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
-
- I18n.t(:"errors.format", {
- :default => "%{attribute} %{message}",
- :attribute => attr_name,
- :message => message
- })
- end
- }
+ map { |attribute, message| full_message(attribute, message) }
+ end
+
+ # Returns a full message for a given attribute.
+ #
+ # company.errors.full_message(:name, "is invalid") # =>
+ # "Name is invalid"
+ def full_message(attribute, message)
+ return message if attribute == :base
+ attr_name = attribute.to_s.gsub('.', '_').humanize
+ attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
+ I18n.t(:"errors.format", {
+ :default => "%{attribute} %{message}",
+ :attribute => attr_name,
+ :message => message
+ })
end
# Translates an error message in its default scope
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 63380d6ffd..7a109d9a52 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -1,5 +1,3 @@
-require 'bcrypt'
-
module ActiveModel
module SecurePassword
extend ActiveSupport::Concern
@@ -12,6 +10,10 @@ module ActiveModel
# a "password_confirmation" attribute) are automatically added.
# You can add more validations by hand if need be.
#
+ # You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use has_secure_password:
+ #
+ # gem 'bcrypt-ruby', '~> 3.0.0'
+ #
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
#
# # Schema: User(name:string, password_digest:string)
@@ -30,6 +32,11 @@ module ActiveModel
# User.find_by_name("david").try(:authenticate, "notright") # => nil
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
def has_secure_password
+ # Load bcrypt-ruby only when has_secured_password is used to avoid make ActiveModel
+ # (and by extension the entire framework) dependent on a binary library.
+ gem 'bcrypt-ruby', '~> 3.0.0'
+ require 'bcrypt'
+
attr_reader :password
validates_confirmation_of :password
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index 85ca8ca835..4c76bb43a8 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -33,6 +33,12 @@ class ErrorsTest < ActiveModel::TestCase
assert errors.include?(:foo), 'errors should include :foo'
end
+ def test_has_key?
+ errors = ActiveModel::Errors.new(self)
+ errors[:foo] = 'omg'
+ assert errors.has_key?(:foo), 'errors should have key :foo'
+ end
+
test "should return true if no errors" do
person = Person.new
person.errors[:foo]
@@ -46,7 +52,6 @@ class ErrorsTest < ActiveModel::TestCase
person.validate!
assert_equal ["name can not be nil"], person.errors.full_messages
assert_equal ["can not be nil"], person.errors[:name]
-
end
test 'should be able to assign error' do
@@ -72,7 +77,6 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.add(:name, "can not be blank")
person.errors.add(:name, "can not be nil")
assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a
-
end
test 'to_hash should return an ordered hash' do
@@ -80,4 +84,33 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.add(:name, "can not be blank")
assert_instance_of ActiveSupport::OrderedHash, person.errors.to_hash
end
+
+ test 'full_messages should return an array of error messages, with the attribute name included' do
+ person = Person.new
+ person.errors.add(:name, "can not be blank")
+ person.errors.add(:name, "can not be nil")
+ assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a
+ end
+
+ test 'full_message should return the given message if attribute equals :base' do
+ person = Person.new
+ assert_equal "press the button", person.errors.full_message(:base, "press the button")
+ end
+
+ test 'full_message should return the given message with the attribute name included' do
+ person = Person.new
+ assert_equal "name can not be blank", person.errors.full_message(:name, "can not be blank")
+ end
+
+ test 'should return a JSON hash representation of the errors' do
+ person = Person.new
+ person.errors.add(:name, "can not be blank")
+ person.errors.add(:name, "can not be nil")
+ person.errors.add(:email, "is invalid")
+ hash = person.errors.as_json
+ assert_equal ["can not be blank", "can not be nil"], hash[:name]
+ assert_equal ["is invalid"], hash[:email]
+ end
+
end
+
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 700e11ff94..e82906186e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,4 +1,14 @@
-*Rails 3.2.0 (unreleased)*
+Wed Sep 7 15:25:02 2011 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache
+ keys are per process id.
+ * lib/active_record/connection_adapters/sqlite_adapter.rb: ditto
+
+* Add first_or_create, first_or_create!, first_or_build and first_or_new methods to Active Record. This is a better approach over the old find_or_create_by dynamic methods because it's clearer which arguments are used to find the record and which are used to create it:
+
+ User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson", :hot => true)
+
+ [Andrés Mejía]
* Support bulk change_table in mysql2 adapter, as well as the mysql one. [Jon Leighton]
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index adb6af7165..58c9648ce8 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -20,6 +20,10 @@ module ActiveRecord
private
+ def find_target?
+ !loaded? && foreign_key_present? && klass
+ end
+
def update_counters(record)
counter_cache_name = reflection.counter_cache_column
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index 81172179e0..b347a94978 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -16,7 +16,7 @@ module ActiveRecord
chain[1..-1].each do |reflection|
scope = scope.merge(
reflection.klass.scoped.with_default_scope.
- except(:select, :create_with, :includes)
+ except(:select, :create_with, :includes, :preload, :joins, :eager_load)
)
end
scope
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 374791deb1..2979ad1cb3 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -442,6 +442,7 @@ module ActiveRecord #:nodoc:
class << self # Class methods
delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
+ delegate :first_or_create, :first_or_create!, :first_or_new, :first_or_build, :to => :scoped
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
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 61994d4a47..20863e73aa 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -421,7 +421,7 @@ module ActiveRecord
# can be used as an argument for establish_connection, for easily
# re-establishing the connection.
def remove_connection(klass)
- pool = @connection_pools[klass.name]
+ pool = @connection_pools.delete(klass.name)
return nil unless pool
pool.automatic_reconnect = false
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
index 7312e34f01..c08c0263b9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -126,7 +126,7 @@ module ActiveRecord
end
def connection_pool
- connection_handler.retrieve_connection_pool(self)
+ connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
end
def retrieve_connection
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 3de850ec9e..f93c7cd74a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -102,10 +102,13 @@ module ActiveRecord
def quoted_date(value)
if value.acts_like?(:time)
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
- value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
- else
- value
- end.to_s(:db)
+
+ if value.respond_to?(zone_conversion_method)
+ value = value.send(zone_conversion_method)
+ end
+ end
+
+ value.to_s(:db)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index e9bdcc2104..a1824fe396 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_mysql_adapter'
+require 'active_record/connection_adapters/statement_pool'
require 'active_support/core_ext/hash/keys'
gem 'mysql', '~> 2.8.1'
@@ -90,9 +91,42 @@ module ActiveRecord
ADAPTER_NAME = 'MySQL'
+ class StatementPool < ConnectionAdapters::StatementPool
+ def initialize(connection, max = 1000)
+ super
+ @cache = Hash.new { |h,pid| h[pid] = {} }
+ end
+
+ def each(&block); cache.each(&block); end
+ def key?(key); cache.key?(key); end
+ def [](key); cache[key]; end
+ def length; cache.length; end
+ def delete(key); cache.delete(key); end
+
+ def []=(sql, key)
+ while @max <= cache.size
+ cache.shift.last[:stmt].close
+ end
+ cache[sql] = key
+ end
+
+ def clear
+ cache.values.each do |hash|
+ hash[:stmt].close
+ end
+ cache.clear
+ end
+
+ private
+ def cache
+ @cache[$$]
+ end
+ end
+
def initialize(connection, logger, connection_options, config)
super
- @statements = {}
+ @statements = StatementPool.new(@connection,
+ config.fetch(:statement_limit) { 1000 })
@client_encoding = nil
connect
end
@@ -187,9 +221,6 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
- @statements.values.each do |cache|
- cache[:stmt].close
- end
@statements.clear
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index ba4a6c7a78..5402918b1d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,5 +1,6 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_support/core_ext/object/blank'
+require 'active_record/connection_adapters/statement_pool'
# Make sure we're using pg high enough for PGResult#values
gem 'pg', '~> 0.11'
@@ -246,6 +247,47 @@ module ActiveRecord
true
end
+ class StatementPool < ConnectionAdapters::StatementPool
+ def initialize(connection, max)
+ super
+ @counter = 0
+ @cache = Hash.new { |h,pid| h[pid] = {} }
+ end
+
+ def each(&block); cache.each(&block); end
+ def key?(key); cache.key?(key); end
+ def [](key); cache[key]; end
+ def length; cache.length; end
+
+ def next_key
+ "a#{@counter + 1}"
+ end
+
+ def []=(sql, key)
+ while @max <= cache.size
+ dealloc(cache.shift.last)
+ end
+ @counter += 1
+ cache[sql] = key
+ end
+
+ def clear
+ cache.each_value do |stmt_key|
+ dealloc stmt_key
+ end
+ cache.clear
+ end
+
+ private
+ def cache
+ @cache[$$]
+ end
+
+ def dealloc(key)
+ @connection.query "DEALLOCATE #{key}"
+ end
+ end
+
# Initializes and connects a PostgreSQL adapter.
def initialize(connection, logger, connection_parameters, config)
super(connection, logger)
@@ -254,9 +296,10 @@ module ActiveRecord
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@local_tz = nil
@table_alias_length = nil
- @statements = {}
connect
+ @statements = StatementPool.new @connection,
+ config.fetch(:statement_limit) { 1000 }
if postgresql_version < 80200
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
@@ -271,9 +314,6 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
- @statements.each_value do |value|
- @connection.query "DEALLOCATE #{value}"
- end
@statements.clear
end
@@ -683,12 +723,12 @@ module ActiveRecord
binds << [nil, schema] if schema
exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0
- SELECT COUNT(*)
- FROM pg_class c
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind in ('v','r')
- AND c.relname = $1
- AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
+ SELECT COUNT(*)
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind in ('v','r')
+ AND c.relname = $1
+ AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
SQL
end
@@ -996,7 +1036,7 @@ module ActiveRecord
def exec_cache(sql, binds)
unless @statements.key? sql
- nextkey = "a#{@statements.length + 1}"
+ nextkey = @statements.next_key
@connection.prepare nextkey, sql
@statements[sql] = nextkey
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 1996e49296..1932a849ee 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,4 +1,6 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_record/connection_adapters/statement_pool'
+require 'active_support/core_ext/string/encoding'
module ActiveRecord
module ConnectionAdapters #:nodoc:
@@ -47,9 +49,45 @@ module ActiveRecord
end
end
+ class StatementPool < ConnectionAdapters::StatementPool
+ def initialize(connection, max)
+ super
+ @cache = Hash.new { |h,pid| h[pid] = {} }
+ end
+
+ def each(&block); cache.each(&block); end
+ def key?(key); cache.key?(key); end
+ def [](key); cache[key]; end
+ def length; cache.length; end
+
+ def []=(sql, key)
+ while @max <= cache.size
+ dealloc(cache.shift.last[:stmt])
+ end
+ cache[sql] = key
+ end
+
+ def clear
+ cache.values.each do |hash|
+ dealloc hash[:stmt]
+ end
+ cache.clear
+ end
+
+ private
+ def cache
+ @cache[$$]
+ end
+
+ def dealloc(stmt)
+ stmt.close unless stmt.closed?
+ end
+ end
+
def initialize(connection, logger, config)
super(connection, logger)
- @statements = {}
+ @statements = StatementPool.new(@connection,
+ config.fetch(:statement_limit) { 1000 })
@config = config
end
@@ -106,10 +144,6 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
- @statements.values.map { |hash| hash[:stmt] }.each { |stmt|
- stmt.close unless stmt.closed?
- }
-
@statements.clear
end
diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
new file mode 100644
index 0000000000..c6b1bc8b5b
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
@@ -0,0 +1,40 @@
+module ActiveRecord
+ module ConnectionAdapters
+ class StatementPool
+ include Enumerable
+
+ def initialize(connection, max = 1000)
+ @connection = connection
+ @max = max
+ end
+
+ def each
+ raise NotImplementedError
+ end
+
+ def key?(key)
+ raise NotImplementedError
+ end
+
+ def [](key)
+ raise NotImplementedError
+ end
+
+ def length
+ raise NotImplementedError
+ end
+
+ def []=(sql, key)
+ raise NotImplementedError
+ end
+
+ def clear
+ raise NotImplementedError
+ end
+
+ def delete(key)
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 3f36dcde14..6f1ec7f9b3 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -24,375 +24,368 @@ end
class FixturesFileNotFound < StandardError; end
-# Fixtures are a way of organizing data that you want to test against; in short, sample data.
-#
-# = Fixture formats
-#
-# Fixtures come in 1 flavor:
-#
-# 1. YAML fixtures
-#
-# == YAML fixtures
-#
-# This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
-# in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
-#
-# Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed
-# in the directory appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is
-# automatically configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
-# The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
-# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a YAML fixture file looks like this:
-#
-# rubyonrails:
-# id: 1
-# name: Ruby on Rails
-# url: http://www.rubyonrails.org
-#
-# google:
-# id: 2
-# name: Google
-# url: http://www.google.com
-#
-# This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
-# indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
-# pleasure.
-#
-# Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
-# See http://yaml.org/type/omap.html
-# for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
-# This is commonly needed for tree structures. Example:
-#
-# --- !omap
-# - parent:
-# id: 1
-# parent_id: NULL
-# title: Parent
-# - child:
-# id: 2
-# parent_id: 1
-# title: Child
-#
-# = Using fixtures in testcases
-#
-# Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
-# fixtures, but first let's take a look at a sample unit test:
-#
-# require 'test_helper'
-#
-# class WebSiteTest < ActiveSupport::TestCase
-# test "web_site_count" do
-# assert_equal 2, WebSite.count
-# end
-# end
-#
-# By default, the <tt>test_helper module</tt> will load all of your fixtures into your test database,
-# so this test will succeed.
-# The testing environment will automatically load the all fixtures into the database before each test.
-# To ensure consistent data, the environment deletes the fixtures before running the load.
-#
-# In addition to being available in the database, the fixture's data may also be accessed by
-# using a special dynamic method, which has the same name as the model, and accepts the
-# name of the fixture to instantiate:
-#
-# test "find" do
-# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
-# end
-#
-# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the following tests:
-#
-# test "find_alt_method_1" do
-# assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
-# end
-#
-# test "find_alt_method_2" do
-# assert_equal "Ruby on Rails", @rubyonrails.news
-# end
-#
-# In order to use these methods to access fixtured data within your testcases, you must specify one of the
-# following in your <tt>ActiveSupport::TestCase</tt>-derived class:
-#
-# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
-# self.use_instantiated_fixtures = true
-#
-# - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
-# self.use_instantiated_fixtures = :no_instances
-#
-# Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
-# traversed in the database to create the fixture hash and/or instance variables. This is expensive for
-# large sets of fixtured data.
-#
-# = Dynamic fixtures with ERB
-#
-# Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
-# mix ERB in with your YAML fixtures to create a bunch of fixtures for load testing, like:
-#
-# <% for i in 1..1000 %>
-# fix_<%= i %>:
-# id: <%= i %>
-# name: guy_<%= 1 %>
-# <% end %>
-#
-# This will create 1000 very simple YAML fixtures.
-#
-# Using ERB, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
-# This is however a feature to be used with some caution. The point of fixtures are that they're
-# stable units of predictable sample data. If you feel that you need to inject dynamic values, then
-# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
-# in fixtures are to be considered a code smell.
-#
-# = Transactional fixtures
-#
-# TestCases can use begin+rollback to isolate their changes to the database instead of having to
-# delete+insert for every test case.
-#
-# class FooTest < ActiveSupport::TestCase
-# self.use_transactional_fixtures = true
-#
-# test "godzilla" do
-# assert !Foo.all.empty?
-# Foo.destroy_all
-# assert Foo.all.empty?
-# end
-#
-# test "godzilla aftermath" do
-# assert !Foo.all.empty?
-# end
-# end
-#
-# If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
-# then you may omit all fixtures declarations in your test cases since all the data's already there
-# and every case rolls back its changes.
-#
-# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
-# access to fixture data for every table that has been loaded through fixtures (depending on the
-# value of +use_instantiated_fixtures+)
-#
-# When *not* to use transactional fixtures:
-#
-# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
-# all parent transactions commit, particularly, the fixtures transaction which is begun in setup
-# and rolled back in teardown. Thus, you won't be able to verify
-# the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
-# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
-# Use InnoDB, MaxDB, or NDB instead.
-#
-# = Advanced YAML Fixtures
-#
-# YAML fixtures that don't specify an ID get some extra features:
-#
-# * Stable, autogenerated IDs
-# * Label references for associations (belongs_to, has_one, has_many)
-# * HABTM associations as inline lists
-# * Autofilled timestamp columns
-# * Fixture label interpolation
-# * Support for YAML defaults
-#
-# == Stable, autogenerated IDs
-#
-# Here, have a monkey fixture:
-#
-# george:
-# id: 1
-# name: George the Monkey
-#
-# reginald:
-# id: 2
-# name: Reginald the Pirate
-#
-# Each of these fixtures has two unique identifiers: one for the database
-# and one for the humans. Why don't we generate the primary key instead?
-# Hashing each fixture's label yields a consistent ID:
-#
-# george: # generated id: 503576764
-# name: George the Monkey
-#
-# reginald: # generated id: 324201669
-# name: Reginald the Pirate
-#
-# Active Record looks at the fixture's model class, discovers the correct
-# primary key, and generates it right before inserting the fixture
-# into the database.
-#
-# The generated ID for a given label is constant, so we can discover
-# any fixture's ID without loading anything, as long as we know the label.
-#
-# == Label references for associations (belongs_to, has_one, has_many)
-#
-# Specifying foreign keys in fixtures can be very fragile, not to
-# mention difficult to read. Since Active Record can figure out the ID of
-# any fixture from its label, you can specify FK's by label instead of ID.
-#
-# === belongs_to
-#
-# Let's break out some more monkeys and pirates.
-#
-# ### in pirates.yml
-#
-# reginald:
-# id: 1
-# name: Reginald the Pirate
-# monkey_id: 1
-#
-# ### in monkeys.yml
-#
-# george:
-# id: 1
-# name: George the Monkey
-# pirate_id: 1
-#
-# Add a few more monkeys and pirates and break this into multiple files,
-# and it gets pretty hard to keep track of what's going on. Let's
-# use labels instead of IDs:
-#
-# ### in pirates.yml
-#
-# reginald:
-# name: Reginald the Pirate
-# monkey: george
-#
-# ### in monkeys.yml
-#
-# george:
-# name: George the Monkey
-# pirate: reginald
-#
-# Pow! All is made clear. Active Record reflects on the fixture's model class,
-# finds all the +belongs_to+ associations, and allows you to specify
-# a target *label* for the *association* (monkey: george) rather than
-# a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
-#
-# ==== Polymorphic belongs_to
-#
-# Supporting polymorphic relationships is a little bit more complicated, since
-# Active Record needs to know what type your association is pointing at. Something
-# like this should look familiar:
-#
-# ### in fruit.rb
-#
-# belongs_to :eater, :polymorphic => true
-#
-# ### in fruits.yml
-#
-# apple:
-# id: 1
-# name: apple
-# eater_id: 1
-# eater_type: Monkey
-#
-# Can we do better? You bet!
-#
-# apple:
-# eater: george (Monkey)
-#
-# Just provide the polymorphic target type and Active Record will take care of the rest.
-#
-# === has_and_belongs_to_many
-#
-# Time to give our monkey some fruit.
-#
-# ### in monkeys.yml
-#
-# george:
-# id: 1
-# name: George the Monkey
-#
-# ### in fruits.yml
-#
-# apple:
-# id: 1
-# name: apple
-#
-# orange:
-# id: 2
-# name: orange
-#
-# grape:
-# id: 3
-# name: grape
-#
-# ### in fruits_monkeys.yml
-#
-# apple_george:
-# fruit_id: 1
-# monkey_id: 1
-#
-# orange_george:
-# fruit_id: 2
-# monkey_id: 1
-#
-# grape_george:
-# fruit_id: 3
-# monkey_id: 1
-#
-# Let's make the HABTM fixture go away.
-#
-# ### in monkeys.yml
-#
-# george:
-# id: 1
-# name: George the Monkey
-# fruits: apple, orange, grape
-#
-# ### in fruits.yml
-#
-# apple:
-# name: apple
-#
-# orange:
-# name: orange
-#
-# grape:
-# name: grape
-#
-# Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
-# on George's fixture, but we could've just as easily specified a list
-# of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
-# the fixture's model class and discovers the +has_and_belongs_to_many+
-# associations.
-#
-# == Autofilled timestamp columns
-#
-# If your table/model specifies any of Active Record's
-# standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
-# they will automatically be set to <tt>Time.now</tt>.
-#
-# If you've set specific values, they'll be left alone.
-#
-# == Fixture label interpolation
-#
-# The label of the current fixture is always available as a column value:
-#
-# geeksomnia:
-# name: Geeksomnia's Account
-# subdomain: $LABEL
-#
-# Also, sometimes (like when porting older join table fixtures) you'll need
-# to be able to get a hold of the identifier for a given label. ERB
-# to the rescue:
-#
-# george_reginald:
-# monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
-# pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
-#
-# == Support for YAML defaults
-#
-# You probably already know how to use YAML to set and reuse defaults in
-# your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
-#
-# DEFAULTS: &DEFAULTS
-# created_on: <%= 3.weeks.ago.to_s(:db) %>
-#
-# first:
-# name: Smurf
-# *DEFAULTS
-#
-# second:
-# name: Fraggle
-# *DEFAULTS
-#
-# Any fixture labeled "DEFAULTS" is safely ignored.
-
module ActiveRecord
+ # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
+ #
+ # They are stored in YAML files, one file per model, which are placed in the directory
+ # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
+ # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
+ # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
+ # like this:
+ #
+ # rubyonrails:
+ # id: 1
+ # name: Ruby on Rails
+ # url: http://www.rubyonrails.org
+ #
+ # google:
+ # id: 2
+ # name: Google
+ # url: http://www.google.com
+ #
+ # This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
+ # is followed by an indented list of key/value pairs in the "key: value" format. Records are
+ # separated by a blank line for your viewing pleasure.
+ #
+ # Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
+ # See http://yaml.org/type/omap.html
+ # for the specification. You will need ordered fixtures when you have foreign key constraints
+ # on keys in the same table. This is commonly needed for tree structures. Example:
+ #
+ # --- !omap
+ # - parent:
+ # id: 1
+ # parent_id: NULL
+ # title: Parent
+ # - child:
+ # id: 2
+ # parent_id: 1
+ # title: Child
+ #
+ # = Using Fixtures in Test Cases
+ #
+ # Since fixtures are a testing construct, we use them in our unit and functional tests. There
+ # are two ways to use the fixtures, but first let's take a look at a sample unit test:
+ #
+ # require 'test_helper'
+ #
+ # class WebSiteTest < ActiveSupport::TestCase
+ # test "web_site_count" do
+ # assert_equal 2, WebSite.count
+ # end
+ # end
+ #
+ # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
+ # so this test will succeed.
+ #
+ # The testing environment will automatically load the all fixtures into the database before each
+ # test. To ensure consistent data, the environment deletes the fixtures before running the load.
+ #
+ # In addition to being available in the database, the fixture's data may also be accessed by
+ # using a special dynamic method, which has the same name as the model, and accepts the
+ # name of the fixture to instantiate:
+ #
+ # test "find" do
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
+ # end
+ #
+ # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
+ # following tests:
+ #
+ # test "find_alt_method_1" do
+ # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
+ # end
+ #
+ # test "find_alt_method_2" do
+ # assert_equal "Ruby on Rails", @rubyonrails.news
+ # end
+ #
+ # In order to use these methods to access fixtured data within your testcases, you must specify one of the
+ # following in your <tt>ActiveSupport::TestCase</tt>-derived class:
+ #
+ # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
+ # self.use_instantiated_fixtures = true
+ #
+ # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
+ # self.use_instantiated_fixtures = :no_instances
+ #
+ # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
+ # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
+ # large sets of fixtured data.
+ #
+ # = Dynamic fixtures with ERB
+ #
+ # Some times you don't care about the content of the fixtures as much as you care about the volume.
+ # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
+ # testing, like:
+ #
+ # <% 1.upto(1000) do |i| %>
+ # fix_<%= i %>:
+ # id: <%= i %>
+ # name: guy_<%= 1 %>
+ # <% end %>
+ #
+ # This will create 1000 very simple fixtures.
+ #
+ # Using ERB, you can also inject dynamic values into your fixtures with inserts like
+ # <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
+ # This is however a feature to be used with some caution. The point of fixtures are that they're
+ # stable units of predictable sample data. If you feel that you need to inject dynamic values, then
+ # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
+ # in fixtures are to be considered a code smell.
+ #
+ # = Transactional Fixtures
+ #
+ # Test cases can use begin+rollback to isolate their changes to the database instead of having to
+ # delete+insert for every test case.
+ #
+ # class FooTest < ActiveSupport::TestCase
+ # self.use_transactional_fixtures = true
+ #
+ # test "godzilla" do
+ # assert !Foo.all.empty?
+ # Foo.destroy_all
+ # assert Foo.all.empty?
+ # end
+ #
+ # test "godzilla aftermath" do
+ # assert !Foo.all.empty?
+ # end
+ # end
+ #
+ # If you preload your test database with all fixture data (probably in the rake task) and use
+ # transactional fixtures, then you may omit all fixtures declarations in your test cases since
+ # all the data's already there and every case rolls back its changes.
+ #
+ # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
+ # true. This will provide access to fixture data for every table that has been loaded through
+ # fixtures (depending on the value of +use_instantiated_fixtures+).
+ #
+ # When *not* to use transactional fixtures:
+ #
+ # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
+ # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
+ # and rolled back in teardown. Thus, you won't be able to verify
+ # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
+ # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
+ # Use InnoDB, MaxDB, or NDB instead.
+ #
+ # = Advanced Fixtures
+ #
+ # Fixtures that don't specify an ID get some extra features:
+ #
+ # * Stable, autogenerated IDs
+ # * Label references for associations (belongs_to, has_one, has_many)
+ # * HABTM associations as inline lists
+ # * Autofilled timestamp columns
+ # * Fixture label interpolation
+ # * Support for YAML defaults
+ #
+ # == Stable, Autogenerated IDs
+ #
+ # Here, have a monkey fixture:
+ #
+ # george:
+ # id: 1
+ # name: George the Monkey
+ #
+ # reginald:
+ # id: 2
+ # name: Reginald the Pirate
+ #
+ # Each of these fixtures has two unique identifiers: one for the database
+ # and one for the humans. Why don't we generate the primary key instead?
+ # Hashing each fixture's label yields a consistent ID:
+ #
+ # george: # generated id: 503576764
+ # name: George the Monkey
+ #
+ # reginald: # generated id: 324201669
+ # name: Reginald the Pirate
+ #
+ # Active Record looks at the fixture's model class, discovers the correct
+ # primary key, and generates it right before inserting the fixture
+ # into the database.
+ #
+ # The generated ID for a given label is constant, so we can discover
+ # any fixture's ID without loading anything, as long as we know the label.
+ #
+ # == Label references for associations (belongs_to, has_one, has_many)
+ #
+ # Specifying foreign keys in fixtures can be very fragile, not to
+ # mention difficult to read. Since Active Record can figure out the ID of
+ # any fixture from its label, you can specify FK's by label instead of ID.
+ #
+ # === belongs_to
+ #
+ # Let's break out some more monkeys and pirates.
+ #
+ # ### in pirates.yml
+ #
+ # reginald:
+ # id: 1
+ # name: Reginald the Pirate
+ # monkey_id: 1
+ #
+ # ### in monkeys.yml
+ #
+ # george:
+ # id: 1
+ # name: George the Monkey
+ # pirate_id: 1
+ #
+ # Add a few more monkeys and pirates and break this into multiple files,
+ # and it gets pretty hard to keep track of what's going on. Let's
+ # use labels instead of IDs:
+ #
+ # ### in pirates.yml
+ #
+ # reginald:
+ # name: Reginald the Pirate
+ # monkey: george
+ #
+ # ### in monkeys.yml
+ #
+ # george:
+ # name: George the Monkey
+ # pirate: reginald
+ #
+ # Pow! All is made clear. Active Record reflects on the fixture's model class,
+ # finds all the +belongs_to+ associations, and allows you to specify
+ # a target *label* for the *association* (monkey: george) rather than
+ # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
+ #
+ # ==== Polymorphic belongs_to
+ #
+ # Supporting polymorphic relationships is a little bit more complicated, since
+ # Active Record needs to know what type your association is pointing at. Something
+ # like this should look familiar:
+ #
+ # ### in fruit.rb
+ #
+ # belongs_to :eater, :polymorphic => true
+ #
+ # ### in fruits.yml
+ #
+ # apple:
+ # id: 1
+ # name: apple
+ # eater_id: 1
+ # eater_type: Monkey
+ #
+ # Can we do better? You bet!
+ #
+ # apple:
+ # eater: george (Monkey)
+ #
+ # Just provide the polymorphic target type and Active Record will take care of the rest.
+ #
+ # === has_and_belongs_to_many
+ #
+ # Time to give our monkey some fruit.
+ #
+ # ### in monkeys.yml
+ #
+ # george:
+ # id: 1
+ # name: George the Monkey
+ #
+ # ### in fruits.yml
+ #
+ # apple:
+ # id: 1
+ # name: apple
+ #
+ # orange:
+ # id: 2
+ # name: orange
+ #
+ # grape:
+ # id: 3
+ # name: grape
+ #
+ # ### in fruits_monkeys.yml
+ #
+ # apple_george:
+ # fruit_id: 1
+ # monkey_id: 1
+ #
+ # orange_george:
+ # fruit_id: 2
+ # monkey_id: 1
+ #
+ # grape_george:
+ # fruit_id: 3
+ # monkey_id: 1
+ #
+ # Let's make the HABTM fixture go away.
+ #
+ # ### in monkeys.yml
+ #
+ # george:
+ # id: 1
+ # name: George the Monkey
+ # fruits: apple, orange, grape
+ #
+ # ### in fruits.yml
+ #
+ # apple:
+ # name: apple
+ #
+ # orange:
+ # name: orange
+ #
+ # grape:
+ # name: grape
+ #
+ # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
+ # on George's fixture, but we could've just as easily specified a list
+ # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
+ # the fixture's model class and discovers the +has_and_belongs_to_many+
+ # associations.
+ #
+ # == Autofilled Timestamp Columns
+ #
+ # If your table/model specifies any of Active Record's
+ # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
+ # they will automatically be set to <tt>Time.now</tt>.
+ #
+ # If you've set specific values, they'll be left alone.
+ #
+ # == Fixture label interpolation
+ #
+ # The label of the current fixture is always available as a column value:
+ #
+ # geeksomnia:
+ # name: Geeksomnia's Account
+ # subdomain: $LABEL
+ #
+ # Also, sometimes (like when porting older join table fixtures) you'll need
+ # to be able to get a hold of the identifier for a given label. ERB
+ # to the rescue:
+ #
+ # george_reginald:
+ # monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
+ # pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
+ #
+ # == Support for YAML defaults
+ #
+ # You probably already know how to use YAML to set and reuse defaults in
+ # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
+ #
+ # DEFAULTS: &DEFAULTS
+ # created_on: <%= 3.weeks.ago.to_s(:db) %>
+ #
+ # first:
+ # name: Smurf
+ # *DEFAULTS
+ #
+ # second:
+ # name: Fraggle
+ # *DEFAULTS
+ #
+ # Any fixture labeled "DEFAULTS" is safely ignored.
class Fixtures
MAX_ID = 2 ** 30 - 1
diff --git a/activerecord/lib/active_record/fixtures/file.rb b/activerecord/lib/active_record/fixtures/file.rb
index 04f494db2c..6bad36abb9 100644
--- a/activerecord/lib/active_record/fixtures/file.rb
+++ b/activerecord/lib/active_record/fixtures/file.rb
@@ -29,11 +29,21 @@ module ActiveRecord
rows.each(&block)
end
+ RESCUE_ERRORS = [ ArgumentError ] # :nodoc:
+
private
+ if defined?(Psych) && defined?(Psych::SyntaxError)
+ RESCUE_ERRORS << Psych::SyntaxError
+ end
+
def rows
return @rows if @rows
- data = YAML.load(render(IO.read(@file)))
+ begin
+ data = YAML.load(render(IO.read(@file)))
+ rescue *RESCUE_ERRORS => error
+ raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
+ end
@rows = data ? validate(data).to_a : []
end
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index 2eeff7e36f..ffee5a081a 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -71,7 +71,7 @@ module ActiveRecord
end
def invert_rename_index(args)
- [:rename_index, args.reverse]
+ [:rename_index, [args.first] + args.last(2).reverse]
end
def invert_rename_column(args)
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 13c41350fb..b3316fd1a2 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -203,11 +203,13 @@ db_namespace = namespace :db do
end
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
file_list = []
- Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file|
- # only files matching "20091231235959_some_name.rb" pattern
- if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
- status = db_list.delete(match_data[1]) ? 'up' : 'down'
- file_list << [status, match_data[1], match_data[2].humanize]
+ ActiveRecord::Migrator.migrations_paths.each do |path|
+ Dir.foreach(path) do |file|
+ # only files matching "20091231235959_some_name.rb" pattern
+ if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
+ status = db_list.delete(match_data[1]) ? 'up' : 'down'
+ file_list << [status, match_data[1], match_data[2].humanize]
+ end
end
end
db_list.map! do |version|
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index a2324039cf..89179779e3 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -226,7 +226,7 @@ module ActiveRecord
if options[:counter_cache] == true
"#{active_record.name.demodulize.underscore.pluralize}_count"
elsif options[:counter_cache]
- options[:counter_cache]
+ options[:counter_cache].to_s
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 15fd1a58c8..d3f1347e03 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -94,6 +94,49 @@ module ActiveRecord
scoping { @klass.create!(*args, &block) }
end
+ # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
+ #
+ # Expects arguments in the same format as <tt>Base.create</tt>.
+ #
+ # ==== Examples
+ # # Find the first user named Penélope or create a new one.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Penélope or create a new one.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Scarlett or create a new one with a particular last name.
+ # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ #
+ # # Find the first user named Scarlett or create a new one with a different last name.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Scarlett').first_or_create do |user|
+ # user.last_name = "O'Hara"
+ # end
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ def first_or_create(attributes = nil, options = {}, &block)
+ first || create(attributes, options, &block)
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
+ #
+ # Expects arguments in the same format as <tt>Base.create!</tt>.
+ def first_or_create!(attributes = nil, options = {}, &block)
+ first || create!(attributes, options, &block)
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
+ #
+ # Expects arguments in the same format as <tt>Base.new</tt>.
+ def first_or_new(attributes = nil, options = {}, &block)
+ first || new(attributes, options, &block)
+ end
+ alias :first_or_build :first_or_new
+
def respond_to?(method, include_private = false)
arel.respond_to?(method, include_private) ||
Array.method_defined?(method) ||
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index ec1176e3dd..2fd89882ff 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -62,7 +62,7 @@ module ActiveRecord
start = options.delete(:start).to_i
batch_size = options.delete(:batch_size) || 1000
- relation = relation.except(:order).order(batch_order).limit(batch_size)
+ relation = relation.reorder(batch_order).limit(batch_size)
records = relation.where(table[primary_key].gteq(start)).all
while records.any?
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 73368aed18..7eeb3dde70 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -114,7 +114,7 @@ module ActiveRecord
def first(*args)
if args.any?
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
- to_a.first(*args)
+ limit(*args).to_a
else
apply_finder_options(args.first).first
end
@@ -134,7 +134,11 @@ module ActiveRecord
def last(*args)
if args.any?
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
- to_a.last(*args)
+ if order_values.empty? && reorder_value.nil?
+ order("#{primary_key} DESC").limit(*args).reverse
+ else
+ to_a.last(*args)
+ end
else
apply_finder_options(args.first).last
end
@@ -180,7 +184,9 @@ module ActiveRecord
# Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"])
# Person.exists?
- def exists?(id = nil)
+ def exists?(id = false)
+ return false if id.nil?
+
id = id.id if ActiveRecord::Base === id
join_dependency = construct_join_dependency_for_association_find
diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
new file mode 100644
index 0000000000..83de90f179
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
@@ -0,0 +1,23 @@
+require 'cases/helper'
+
+module ActiveRecord::ConnectionAdapters
+ class MysqlAdapter
+ class StatementPoolTest < ActiveRecord::TestCase
+ def test_cache_is_per_pid
+ return skip('must support fork') unless Process.respond_to?(:fork)
+
+ cache = StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
+
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
+
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 4c6d865d59..76c73e9dfa 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -91,6 +91,12 @@ class SchemaTest < ActiveRecord::TestCase
end
end
+ def test_table_exists_quoted_table
+ with_schema_search_path(SCHEMA_NAME) do
+ assert(@connection.table_exists?('"things.table"'), "table should exist")
+ end
+ end
+
def test_with_schema_prefixed_table_name
assert_nothing_raised do
assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}")
diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
new file mode 100644
index 0000000000..a82c6f67d6
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
@@ -0,0 +1,23 @@
+require 'cases/helper'
+
+module ActiveRecord::ConnectionAdapters
+ class PostgreSQLAdapter < AbstractAdapter
+ class StatementPoolTest < ActiveRecord::TestCase
+ def test_cache_is_per_pid
+ return skip('must support fork') unless Process.respond_to?(:fork)
+
+ cache = StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
+
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
+
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
new file mode 100644
index 0000000000..ae272e2c4b
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
@@ -0,0 +1,24 @@
+require 'cases/helper'
+
+module ActiveRecord::ConnectionAdapters
+ class SQLiteAdapter
+ class StatementPoolTest < ActiveRecord::TestCase
+ def test_cache_is_per_pid
+ return skip('must support fork') unless Process.respond_to?(:fork)
+
+ cache = StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
+
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
+
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
+ end
+ end
+ end
+end
+
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 818902beb5..866a3cca10 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -352,6 +352,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal members(:groucho), sponsor.sponsorable
end
+ def test_dont_find_target_when_foreign_key_is_null
+ tagging = taggings(:thinking_general)
+ queries = assert_sql { tagging.super_tag }
+ assert_equal 0, queries.length
+ end
+
def test_field_name_same_as_foreign_key
computer = Computer.find(1)
assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index 490fc5177e..8dc1423375 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -36,6 +36,11 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
def test_marshalling_extensions
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
+ "to be a Ruby bug.")
+ end
+
david = developers(:david)
assert_equal projects(:action_controller), david.projects.find_most_recent
@@ -46,6 +51,11 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
def test_marshalling_named_extensions
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
+ "to be a Ruby bug.")
+ end
+
david = developers(:david)
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index a2764f3e3b..1e59931963 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -17,6 +17,7 @@ require 'models/invoice'
require 'models/line_item'
require 'models/car'
require 'models/bulb'
+require 'models/engine'
class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase
class Invoice < ActiveRecord::Base
@@ -850,6 +851,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_clearing_updates_counter_cache_when_inverse_counter_cache_is_a_symbol_with_dependent_destroy
+ car = Car.first
+ car.engines.create!
+
+ assert_difference 'car.reload.engines_count', -1 do
+ car.engines.clear
+ end
+ end
+
def test_clearing_a_dependent_association_collection
firm = companies(:first_firm)
client_id = firm.dependent_clients_of_firm.first.id
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 80c6e41169..530f5212a2 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -356,6 +356,17 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal categories(:general), members(:groucho).club_category
end
+ def test_joins_and_includes_from_through_models_not_included_in_association
+ prev_default_scope = Club.default_scopes
+
+ [:includes, :preload, :joins, :eager_load].each do |q|
+ Club.default_scopes = [Club.send(q, :category)]
+ assert_equal categories(:general), members(:groucho).reload.club_category
+ end
+ ensure
+ Club.default_scopes = prev_default_scope
+ end
+
def test_has_one_through_has_one_through_with_belongs_to_source_reflection_preload
members = assert_queries(4) { Member.includes(:club_category).to_a.sort_by(&:id) }
general = categories(:general)
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index bee183cc67..cb92f79e0e 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -22,8 +22,10 @@ require 'models/person'
require 'models/edge'
require 'models/joke'
require 'models/bulb'
+require 'models/bird'
require 'rexml/document'
require 'active_support/core_ext/exception'
+require 'bcrypt'
class Category < ActiveRecord::Base; end
class Categorization < ActiveRecord::Base; end
@@ -81,7 +83,13 @@ class BasicsTest < ActiveRecord::TestCase
}
quoted = conn.quote_column_name "foo#{badchar}bar"
- assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted)
+ if current_adapter?(:OracleAdapter)
+ # Oracle does not allow double quotes in table and column names at all
+ # therefore quoting removes them
+ assert_equal("#{badchar}foobar#{badchar}", quoted)
+ else
+ assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted)
+ end
end
def test_columns_should_obey_set_primary_key
@@ -271,6 +279,37 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(true, cb.frickinawesome)
end
+ def test_first_or_create
+ parrot = Bird.first_or_create(:color => 'green', :name => 'parrot')
+ assert parrot.persisted?
+ the_same_parrot = Bird.first_or_create(:color => 'yellow', :name => 'macaw')
+ assert_equal parrot, the_same_parrot
+ end
+
+ def test_first_or_create_bang
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.first_or_create! }
+ parrot = Bird.first_or_create!(:color => 'green', :name => 'parrot')
+ assert parrot.persisted?
+ the_same_parrot = Bird.first_or_create!(:color => 'yellow', :name => 'macaw')
+ assert_equal parrot, the_same_parrot
+ end
+
+ def test_first_or_new
+ parrot = Bird.first_or_new(:color => 'green', :name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.new_record?
+ assert parrot.valid?
+ end
+
+ def test_first_or_build
+ parrot = Bird.first_or_build(:color => 'green', :name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.new_record?
+ assert parrot.valid?
+ end
+
def test_load
topics = Topic.find(:all, :order => 'id')
assert_equal(4, topics.size)
@@ -1828,6 +1867,11 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshal_round_trip
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
+ "to be a Ruby bug.")
+ end
+
expected = posts(:welcome)
marshalled = Marshal.dump(expected)
actual = Marshal.load(marshalled)
@@ -1836,6 +1880,11 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshal_new_record_round_trip
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
+ "to be a Ruby bug.")
+ end
+
marshalled = Marshal.dump(Post.new)
post = Marshal.load(marshalled)
@@ -1843,6 +1892,11 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshalling_with_associations
+ if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
+ return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
+ "to be a Ruby bug.")
+ end
+
post = Post.new
post.comments.build
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index a35baee4ed..660098b9ad 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -113,7 +113,27 @@ class EachTest < ActiveRecord::TestCase
batch.map! { not_a_post }
end
end
+ end
+ def test_find_in_batches_should_ignore_the_order_default_scope
+ # First post is with title scope
+ first_post = PostWithDefaultScope.first
+ posts = []
+ PostWithDefaultScope.find_in_batches do |batch|
+ posts.concat(batch)
+ end
+ # posts.first will be ordered using id only. Title order scope should not apply here
+ assert_not_equal first_post, posts.first
+ assert_equal posts(:welcome), posts.first
+ end
+
+ def test_find_in_batches_should_not_ignore_the_default_scope_if_it_is_other_then_order
+ special_posts_ids = SpecialPostWithDefaultScope.all.map(&:id).sort
+ posts = []
+ SpecialPostWithDefaultScope.find_in_batches do |batch|
+ posts.concat(batch)
+ end
+ assert_equal special_posts_ids, posts.map(&:id)
end
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index abf317768f..bd0d161838 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -6,7 +6,12 @@ module ActiveRecord
def setup
@handler = ConnectionHandler.new
@handler.establish_connection 'america', Base.connection_pool.spec
- @klass = Struct.new(:name).new('america')
+ @klass = Class.new do
+ def self.name; 'america'; end
+ end
+ @subklass = Class.new(@klass) do
+ def self.name; 'north america'; end
+ end
end
def test_retrieve_connection
@@ -28,6 +33,20 @@ module ActiveRecord
def test_retrieve_connection_pool
assert_not_nil @handler.retrieve_connection_pool(@klass)
end
+
+ def test_retrieve_connection_pool_uses_superclass_when_no_subclass_connection
+ assert_not_nil @handler.retrieve_connection_pool(@subklass)
+ end
+
+ def test_retrieve_connection_pool_uses_superclass_pool_after_subclass_establish_and_remove
+ @handler.establish_connection 'north america', Base.connection_pool.spec
+ assert_not_same @handler.retrieve_connection_pool(@klass),
+ @handler.retrieve_connection_pool(@subklass)
+
+ @handler.remove_connection @subklass
+ assert_same @handler.retrieve_connection_pool(@klass),
+ @handler.retrieve_connection_pool(@subklass)
+ end
end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 5dc5f99582..3088ab012f 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -48,6 +48,15 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.exists?
end
+ # exists? should handle nil for id's that come from URLs and always return false
+ # (example: Topic.exists?(params[:id])) where params[:id] is nil
+ def test_exists_with_nil_arg
+ assert !Topic.exists?(nil)
+ assert Topic.exists?
+ assert !Topic.first.replies.exists?(nil)
+ assert Topic.first.replies.exists?
+ end
+
def test_does_not_exist_with_empty_table_and_no_args_given
Topic.delete_all
assert !Topic.exists?
@@ -243,6 +252,32 @@ class FinderTest < ActiveRecord::TestCase
end
end
+ def test_first_and_last_with_integer_should_use_sql_limit
+ assert_sql(/LIMIT 2|ROWNUM <= 2/) { Topic.first(2).entries }
+ assert_sql(/LIMIT 5|ROWNUM <= 5/) { Topic.last(5).entries }
+ end
+
+ def test_last_with_integer_and_order_should_keep_the_order
+ assert_equal Topic.order("title").to_a.last(2), Topic.order("title").last(2)
+ end
+
+ def test_last_with_integer_and_order_should_not_use_sql_limit
+ query = assert_sql { Topic.order("title").last(5).entries }
+ assert_equal 1, query.length
+ assert_no_match(/LIMIT/, query.first)
+ end
+
+ def test_last_with_integer_and_reorder_should_not_use_sql_limit
+ query = assert_sql { Topic.reorder("title").last(5).entries }
+ assert_equal 1, query.length
+ assert_no_match(/LIMIT/, query.first)
+ end
+
+ def test_first_and_last_with_integer_should_return_an_array
+ assert_kind_of Array, Topic.first(5)
+ assert_kind_of Array, Topic.last(5)
+ end
+
def test_unexisting_record_exception_handling
assert_raise(ActiveRecord::RecordNotFound) {
Topic.find(1).parent
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 913f6a3340..866dcefbab 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -20,6 +20,7 @@ require 'models/book'
require 'models/admin'
require 'models/admin/account'
require 'models/admin/user'
+require 'tempfile'
class FixturesTest < ActiveRecord::TestCase
self.use_instantiated_fixtures = true
@@ -45,6 +46,21 @@ class FixturesTest < ActiveRecord::TestCase
end
end
+ def test_broken_yaml_exception
+ badyaml = Tempfile.new ['foo', '.yml']
+ badyaml.write 'a: !ruby.yaml.org,2002:str |\nfoo'
+ badyaml.flush
+
+ dir = File.dirname badyaml.path
+ name =File.basename badyaml.path, '.yml'
+ assert_raises(ActiveRecord::Fixture::FormatError) do
+ ActiveRecord::Fixtures.create_fixtures(dir, name)
+ end
+ ensure
+ badyaml.close
+ badyaml.unlink
+ end
+
def test_create_fixtures
ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots")
assert Parrot.find_by_name('Curious George'), 'George is in the database'
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 36007255fa..d108b456f0 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -104,9 +104,9 @@ module ActiveRecord
end
def test_invert_rename_index
- @recorder.record :rename_index, [:old, :new]
+ @recorder.record :rename_index, [:table, :old, :new]
rename = @recorder.inverse.first
- assert_equal [:rename_index, [:new, :old]], rename
+ assert_equal [:rename_index, [:table, :new, :old]], rename
end
def test_invert_add_timestamps
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index ed0240cada..4a09a87322 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -182,7 +182,7 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_first_and_last_should_allow_integers_for_limit
assert_equal Topic.base.first(2), Topic.base.to_a.first(2)
- assert_equal Topic.base.last(2), Topic.base.to_a.last(2)
+ assert_equal Topic.base.last(2), Topic.base.order("id").to_a.last(2)
end
def test_first_and_last_should_not_use_query_when_results_are_loaded
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 615551a279..0f50ac9a2b 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -863,6 +863,134 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 'hen', hen.name
end
+ def test_first_or_create
+ parrot = Bird.where(:color => 'green').first_or_create(:name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'parrot', parrot.name
+ assert_equal 'green', parrot.color
+
+ same_parrot = Bird.where(:color => 'green').first_or_create(:name => 'parakeet')
+ assert_kind_of Bird, same_parrot
+ assert same_parrot.persisted?
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_with_no_parameters
+ parrot = Bird.where(:color => 'green').first_or_create
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert_equal 'green', parrot.color
+ end
+
+ def test_first_or_create_with_block
+ parrot = Bird.where(:color => 'green').first_or_create { |bird| bird.name = 'parrot' }
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'green', parrot.color
+ assert_equal 'parrot', parrot.name
+
+ same_parrot = Bird.where(:color => 'green').first_or_create { |bird| bird.name = 'parakeet' }
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_with_array
+ several_green_birds = Bird.where(:color => 'green').first_or_create([{:name => 'parrot'}, {:name => 'parakeet'}])
+ assert_kind_of Array, several_green_birds
+ several_green_birds.each { |bird| assert bird.persisted? }
+
+ same_parrot = Bird.where(:color => 'green').first_or_create([{:name => 'hummingbird'}, {:name => 'macaw'}])
+ assert_kind_of Bird, same_parrot
+ assert_equal several_green_birds.first, same_parrot
+ end
+
+ def test_first_or_create_bang_with_valid_options
+ parrot = Bird.where(:color => 'green').first_or_create!(:name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'parrot', parrot.name
+ assert_equal 'green', parrot.color
+
+ same_parrot = Bird.where(:color => 'green').first_or_create!(:name => 'parakeet')
+ assert_kind_of Bird, same_parrot
+ assert same_parrot.persisted?
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_bang_with_invalid_options
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create!(:pirate_id => 1) }
+ end
+
+ def test_first_or_create_bang_with_no_parameters
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create! }
+ end
+
+ def test_first_or_create_bang_with_valid_block
+ parrot = Bird.where(:color => 'green').first_or_create! { |bird| bird.name = 'parrot' }
+ assert_kind_of Bird, parrot
+ assert parrot.persisted?
+ assert_equal 'green', parrot.color
+ assert_equal 'parrot', parrot.name
+
+ same_parrot = Bird.where(:color => 'green').first_or_create! { |bird| bird.name = 'parakeet' }
+ assert_equal parrot, same_parrot
+ end
+
+ def test_first_or_create_bang_with_invalid_block
+ assert_raise(ActiveRecord::RecordInvalid) do
+ Bird.where(:color => 'green').first_or_create! { |bird| bird.pirate_id = 1 }
+ end
+ end
+
+ def test_first_or_create_with_valid_array
+ several_green_birds = Bird.where(:color => 'green').first_or_create!([{:name => 'parrot'}, {:name => 'parakeet'}])
+ assert_kind_of Array, several_green_birds
+ several_green_birds.each { |bird| assert bird.persisted? }
+
+ same_parrot = Bird.where(:color => 'green').first_or_create!([{:name => 'hummingbird'}, {:name => 'macaw'}])
+ assert_kind_of Bird, same_parrot
+ assert_equal several_green_birds.first, same_parrot
+ end
+
+ def test_first_or_create_with_invalid_array
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create!([ {:name => 'parrot'}, {:pirate_id => 1} ]) }
+ end
+
+ def test_first_or_new
+ parrot = Bird.where(:color => 'green').first_or_new(:name => 'parrot')
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.valid?
+ assert parrot.new_record?
+ assert_equal 'parrot', parrot.name
+ assert_equal 'green', parrot.color
+ end
+
+ def test_first_or_new_with_no_parameters
+ parrot = Bird.where(:color => 'green').first_or_new
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert !parrot.valid?
+ assert parrot.new_record?
+ assert_equal 'green', parrot.color
+ end
+
+ def test_first_or_new_with_block
+ parrot = Bird.where(:color => 'green').first_or_new { |bird| bird.name = 'parrot' }
+ assert_kind_of Bird, parrot
+ assert !parrot.persisted?
+ assert parrot.valid?
+ assert parrot.new_record?
+ assert_equal 'green', parrot.color
+ assert_equal 'parrot', parrot.name
+ end
+
+ def test_first_or_build_is_alias_for_first_or_new
+ birds = Bird.scoped
+ assert birds.respond_to?(:first_or_build)
+ assert_equal birds.method(:first_or_new), birds.method(:first_or_build)
+ end
+
def test_explicit_create_scope
hens = Bird.where(:name => 'hen')
assert_equal 'hen', hens.new.name
@@ -1018,7 +1146,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_update_all_with_joins_and_offset_and_order
- all_comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id).order('posts.id')
+ all_comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id).order('posts.id', 'comments.id')
count = all_comments.count
comments = all_comments.offset(1)
diff --git a/activerecord/test/fixtures/tasks.yml b/activerecord/test/fixtures/tasks.yml
index 01c95b3a4c..402ca85faf 100644
--- a/activerecord/test/fixtures/tasks.yml
+++ b/activerecord/test/fixtures/tasks.yml
@@ -1,4 +1,4 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
first_task:
id: 1
starting: 2005-03-30t06:30:00.00+01:00
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index 76f20b1061..b9c2e8ec9a 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -8,7 +8,7 @@ class Car < ActiveRecord::Base
has_one :frickinawesome_bulb, :class_name => "Bulb", :conditions => { :frickinawesome => true }
has_many :tyres
- has_many :engines
+ has_many :engines, :dependent => :destroy
has_many :wheels, :as => :wheelable
scope :incl_tyres, includes(:tyres)
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index affa37b02d..198a963cbc 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -171,4 +171,14 @@ class PostWithDefaultInclude < ActiveRecord::Base
self.table_name = 'posts'
default_scope includes(:comments)
has_many :comments, :foreign_key => :post_id
+end
+
+class PostWithDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope :order => :title
+end
+
+class SpecialPostWithDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope where(:id => [1, 5,6])
end \ No newline at end of file
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index 236dc565f3..1ffd83b91d 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -955,7 +955,7 @@ module ActiveResource
prefix_options, query_options = {}, {}
(options || {}).each do |key, value|
- next if key.blank?
+ next if key.blank? || !key.respond_to?(:to_sym)
(prefix_parameters.include?(key.to_sym) ? prefix_options : query_options)[key.to_sym] = value
end
diff --git a/activeresource/test/cases/base/load_test.rb b/activeresource/test/cases/base/load_test.rb
index 0d030148d0..784e7dd036 100644
--- a/activeresource/test/cases/base/load_test.rb
+++ b/activeresource/test/cases/base/load_test.rb
@@ -51,9 +51,28 @@ class BaseLoadTest < Test::Unit::TestCase
:votes => [ true, false, true ],
:places => [ "Columbia City", "Unknown" ]}}}
+
+ # List of books formated as [{timestamp_of_publication => name}, ...]
+ @books = {:books => [
+ {1009839600 => "Ruby in a Nutshell"},
+ {1199142000 => "The Ruby Programming Language"}
+ ]}
+
+ @books_date = {:books => [
+ {Time.at(1009839600) => "Ruby in a Nutshell"},
+ {Time.at(1199142000) => "The Ruby Programming Language"}
+ ]}
@person = Person.new
end
+ def test_load_hash_with_integers_as_keys
+ assert_nothing_raised{@person.load(@books)}
+ end
+
+ def test_load_hash_with_dates_as_keys
+ assert_nothing_raised{@person.load(@books_date)}
+ end
+
def test_load_expects_hash
assert_raise(ArgumentError) { @person.load nil }
assert_raise(ArgumentError) { @person.load '<person id="1"/>' }
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 3508ec0f34..18164234a5 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Rails 3.2.0 (unreleased)*
+* ActiveSupport::OrderedHash is now marked as extractable when using Array#extract_options! [Prem Sichanugrist]
+
* Added Array#prepend as an alias for Array#unshift and Array#append as an alias for Array#<< [DHH]
* The definition of blank string for Ruby 1.9 has been extended to Unicode whitespace.
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index ddb4f3012f..9343bb7106 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -97,7 +97,7 @@ module Enumerable
end
# Returns true if the enumerable has more than 1 element. Functionally equivalent to enum.to_a.size > 1.
- # Can be called with a block too, much like any?, so people.many? { |p| p.age > 26 } returns true if more than 1 person is over 26.
+ # Can be called with a block too, much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns true if more than one person is over 26.
def many?
cnt = 0
if block_given?
@@ -110,7 +110,7 @@ module Enumerable
end
end
- # The negative of the Enumerable#include?. Returns true if the collection does not include the object.
+ # The negative of the <tt>Enumerable#include?</tt>. Returns true if the collection does not include the object.
def exclude?(object)
!include?(object)
end
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index 0b368fe7b7..f4cb445444 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -2,7 +2,7 @@ require 'active_support/hash_with_indifferent_access'
class Hash
- # Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
+ # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
#
# {:a => 1}.with_indifferent_access["a"] # => 1
#
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index f111c8e5a3..05b39b89bf 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -143,11 +143,11 @@ module ActiveSupport #:nodoc:
UNSAFE_STRING_METHODS.each do |unsafe_method|
class_eval <<-EOT, __FILE__, __LINE__
- def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
+ def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
to_str.#{unsafe_method}(*args, &block) # to_str.gsub(*args, &block)
end # end
- def #{unsafe_method}!(*args) # def gsub!(*args)
+ def #{unsafe_method}!(*args) # def capitalize!(*args)
@dirty = true # @dirty = true
super # super
end # end
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index d9d5e02778..49ac18d245 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -55,9 +55,31 @@ class Time
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
+ # Converts a Time object to a Date, dropping hour, minute, and second precision.
+ #
+ # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007
+ # my_time.to_date # => Mon, 12 Nov 2007
+ #
+ # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
+ # your_time.to_date # => Tue, 13 Jan 2009
+ def to_date
+ ::Date.new(year, month, day)
+ end unless method_defined?(:to_date)
+
# A method to keep Time, Date and DateTime instances interchangeable on conversions.
# In this case, it simply returns +self+.
def to_time
self
end unless method_defined?(:to_time)
+
+ # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
+ #
+ # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007
+ # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500
+ #
+ # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
+ # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500
+ def to_datetime
+ ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400))
+ end unless method_defined?(:to_datetime)
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 59ffd24698..636f019cd5 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -112,7 +112,7 @@ module ActiveSupport
end
end
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
# Does not overwrite the existing hash.
def merge(hash)
self.dup.update(hash)
diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index 3e54134c5c..dcfcf0b63c 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -18,8 +18,8 @@ module ActiveSupport
# Developer.all
# wait
# assert_equal 1, @logger.logged(:debug).size
- # assert_match /Developer Load/, @logger.logged(:debug).last
- # assert_match /SELECT \* FROM "developers"/, @logger.logged(:debug).last
+ # assert_match(/Developer Load/, @logger.logged(:debug).last)
+ # assert_match(/SELECT \* FROM "developers"/, @logger.logged(:debug).last)
# end
# end
#
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index 7f70628933..0264133581 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -47,6 +47,11 @@ module ActiveSupport
self
end
+ # Returns true to make sure that this hash is extractable via <tt>Array#extract_options!</tt>
+ def extractable_options?
+ true
+ end
+
# Hash is ordered in Ruby 1.9!
if RUBY_VERSION < '1.9'
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 4fb487ade1..35f400f9df 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -371,7 +371,7 @@ module ActiveSupport
protected
def require_tzinfo
- require 'tzinfo'
+ require 'tzinfo' unless defined?(::TZInfo)
rescue LoadError
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
raise
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 449d3810e2..f11bf3dc69 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -183,8 +183,9 @@ class ModuleTest < Test::Unit::TestCase
someone.foo
rescue NoMethodError => e
file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE}"
- assert e.backtrace.first.include?(file_and_line),
- "[#{e.backtrace.first}] did not include [#{file_and_line}]"
+ # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace.
+ assert e.backtrace.any?{|a| a.include?(file_and_line)},
+ "[#{e.backtrace.inspect}] did not include [#{file_and_line}]"
end
def test_delegation_exception_backtrace_with_allow_nil
@@ -192,8 +193,9 @@ class ModuleTest < Test::Unit::TestCase
someone.bar
rescue NoMethodError => e
file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE_2}"
- assert e.backtrace.first.include?(file_and_line),
- "[#{e.backtrace.first}] did not include [#{file_and_line}]"
+ # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace.
+ assert e.backtrace.any?{|a| a.include?(file_and_line)},
+ "[#{e.backtrace.inspect}] did not include [#{file_and_line}]"
end
def test_parent
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index bf851dbcbc..0b5f912dc4 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -329,4 +329,9 @@ class OrderedHashTest < Test::Unit::TestCase
assert_equal expected, @ordered_hash.invert
assert_equal @values.zip(@keys), @ordered_hash.invert.to_a
end
+
+ def test_extractable
+ @ordered_hash[:rails] = "snowman"
+ assert_equal @ordered_hash, [1, 2, @ordered_hash].extract_options!
+ end
end
diff --git a/bin/rails b/bin/rails
deleted file mode 100755
index f9725d78d0..0000000000
--- a/bin/rails
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env ruby
-
-if File.exists?(File.join(File.expand_path('../..', __FILE__), '.git'))
- railties_path = File.expand_path('../../railties/lib', __FILE__)
- $:.unshift(railties_path)
-end
-require "rails/cli"
diff --git a/rails.gemspec b/rails.gemspec
index bb3fe85c2b..3377b4e175 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
s.homepage = 'http://www.rubyonrails.org'
s.bindir = 'bin'
- s.executables = ['rails']
+ s.executables = []
s.add_dependency('activesupport', version)
s.add_dependency('actionpack', version)
diff --git a/railties/README.rdoc b/railties/README.rdoc
index eb7ed961e3..501541eb06 100644
--- a/railties/README.rdoc
+++ b/railties/README.rdoc
@@ -1,12 +1,12 @@
= Railties -- Gluing the Engine to the Rails
-Railties is responsible to glue all frameworks together. Overall, it:
+Railties is responsible for gluing all frameworks together. Overall, it:
-* handles all the bootstrapping process for a Rails application;
+* handles the bootstrapping process for a Rails application;
-* manages rails command line interface;
+* manages the +rails+ command line interface;
-* provides Rails generators core;
+* and provides Rails generators core.
== Download
diff --git a/railties/Rakefile b/railties/Rakefile
index be9a77d4e4..25e515e016 100755
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -17,8 +17,13 @@ namespace :test do
dir = ENV["TEST_DIR"] || "**"
Dir["test/#{dir}/*_test.rb"].each do |file|
next true if file.include?("fixtures")
- ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
- sh(ruby, '-Itest', "-I#{File.dirname(__FILE__)}/../activesupport/lib", file)
+ dash_i = [
+ 'test',
+ 'lib',
+ "#{File.dirname(__FILE__)}/../activesupport/lib",
+ "#{File.dirname(__FILE__)}/../actionpack/lib"
+ ]
+ ruby "-I#{dash_i.join ':'}", file
end
end
end
diff --git a/railties/bin/rails b/railties/bin/rails
index a7d6938e0d..a1c4faaa73 100755
--- a/railties/bin/rails
+++ b/railties/bin/rails
@@ -1,2 +1,7 @@
#!/usr/bin/env ruby
-require "rails/cli" \ No newline at end of file
+
+if File.exists?(File.join(File.expand_path('../../..', __FILE__), '.git'))
+ railties_path = File.expand_path('../../lib', __FILE__)
+ $:.unshift(railties_path)
+end
+require "rails/cli"
diff --git a/railties/guides/code/getting_started/Gemfile b/railties/guides/code/getting_started/Gemfile
new file mode 100644
index 0000000000..51774934cd
--- /dev/null
+++ b/railties/guides/code/getting_started/Gemfile
@@ -0,0 +1,32 @@
+source 'http://rubygems.org'
+
+gem 'rails', '3.1.0'
+# Bundle edge Rails instead:
+# gem 'rails', :git => 'git://github.com/rails/rails.git'
+
+gem 'sqlite3'
+
+
+# Gems used only for assets and not required
+# in production environments by default.
+group :assets do
+ gem 'sass-rails', " ~> 3.1.0"
+ gem 'coffee-rails', "~> 3.1.0"
+ gem 'uglifier'
+end
+
+gem 'jquery-rails'
+
+# Use unicorn as the web server
+# gem 'unicorn'
+
+# Deploy with Capistrano
+# gem 'capistrano'
+
+# To use debugger
+# gem 'ruby-debug19', :require => 'ruby-debug'
+
+group :test do
+ # Pretty printed test output
+ gem 'turn', :require => false
+end
diff --git a/railties/guides/code/getting_started/README b/railties/guides/code/getting_started/README
new file mode 100644
index 0000000000..7c36f2356e
--- /dev/null
+++ b/railties/guides/code/getting_started/README
@@ -0,0 +1,261 @@
+== 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, create a new Rails application:
+ <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
+
+2. Change directory to <tt>myapp</tt> and start the web server:
+ <tt>cd myapp; rails server</tt> (run with --help for options)
+
+3. Go to http://localhost:3000/ and you'll see:
+ "Welcome aboard: You're riding Ruby on Rails!"
+
+4. Follow the guidelines to start developing your application. You can find
+the following resources handy:
+
+* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
+* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
+
+
+== 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/. There are
+several books available online as well:
+
+* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
+* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
+
+These two 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 <tt>sudo gem install ruby-debug</tt>. Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.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", "body"=>"Only ten..", "id"=>"2"}>]"
+ >> @posts.first.title = "hello from a debugger"
+ => "hello from a debugger"
+
+...and even better, 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 can enter "cont".
+
+
+== Console
+
+The console is a Ruby shell, which allows you to interact with your
+application's domain model. Here you'll have all parts of the application
+configured, just like it is when the application is running. You can inspect
+domain models, change values, and save to the database. Starting the script
+without arguments will launch it in the development environment.
+
+To start the console, run <tt>rails console</tt> from the application
+directory.
+
+Options:
+
+* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
+ made to the database.
+* Passing an environment name as an argument will load the corresponding
+ environment. Example: <tt>rails console production</tt>.
+
+To reload your controllers and models after launching the console run
+<tt>reload!</tt>
+
+More information about irb can be found at:
+link:http://www.rubycentral.org/pickaxe/irb.html
+
+
+== dbconsole
+
+You can go to the command line of your database directly through <tt>rails
+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>rails dbconsole production</tt>. Currently works for MySQL,
+PostgreSQL and SQLite 3.
+
+== Description of Contents
+
+The default directory structure of a generated Ruby on Rails application:
+
+ |-- app
+ | |-- assets
+ | |-- images
+ | |-- javascripts
+ | `-- stylesheets
+ | |-- controllers
+ | |-- helpers
+ | |-- mailers
+ | |-- models
+ | `-- views
+ | `-- layouts
+ |-- config
+ | |-- environments
+ | |-- initializers
+ | `-- locales
+ |-- db
+ |-- doc
+ |-- lib
+ | `-- tasks
+ |-- log
+ |-- public
+ |-- script
+ |-- test
+ | |-- fixtures
+ | |-- functional
+ | |-- integration
+ | |-- performance
+ | `-- unit
+ |-- tmp
+ | |-- cache
+ | |-- pids
+ | |-- sessions
+ | `-- sockets
+ `-- vendor
+ |-- assets
+ `-- stylesheets
+ `-- plugins
+
+app
+ Holds all the code that's specific to this particular application.
+
+app/assets
+ Contains subdirectories for images, stylesheets, and JavaScript files.
+
+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. Models descend from
+ ActiveRecord::Base by default.
+
+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 by default.
+
+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 generators 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. 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 rails generate
+ command, 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/guides/code/getting_started/Rakefile b/railties/guides/code/getting_started/Rakefile
new file mode 100644
index 0000000000..e1d1ec8615
--- /dev/null
+++ b/railties/guides/code/getting_started/Rakefile
@@ -0,0 +1,7 @@
+#!/usr/bin/env rake
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+
+Blog::Application.load_tasks
diff --git a/railties/guides/code/getting_started/app/assets/images/rails.png b/railties/guides/code/getting_started/app/assets/images/rails.png
new file mode 100644
index 0000000000..d5edc04e65
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/images/rails.png
Binary files differ
diff --git a/railties/guides/code/getting_started/app/assets/javascripts/application.js b/railties/guides/code/getting_started/app/assets/javascripts/application.js
new file mode 100644
index 0000000000..37c7bfcdb5
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/javascripts/application.js
@@ -0,0 +1,9 @@
+// This is a manifest file that'll be compiled into including all the files listed below.
+// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// be included in the compiled file accessible from http://example.com/assets/application.js
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// the compiled file.
+//
+//= require jquery
+//= require jquery_ujs
+//= require_tree .
diff --git a/railties/guides/code/getting_started/app/assets/javascripts/comments.js.coffee b/railties/guides/code/getting_started/app/assets/javascripts/comments.js.coffee
new file mode 100644
index 0000000000..761567942f
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/javascripts/comments.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/railties/guides/code/getting_started/app/assets/javascripts/home.js.coffee b/railties/guides/code/getting_started/app/assets/javascripts/home.js.coffee
new file mode 100644
index 0000000000..761567942f
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/javascripts/home.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/railties/guides/code/getting_started/app/assets/javascripts/posts.js.coffee b/railties/guides/code/getting_started/app/assets/javascripts/posts.js.coffee
new file mode 100644
index 0000000000..761567942f
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/javascripts/posts.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/railties/guides/code/getting_started/app/assets/stylesheets/application.css b/railties/guides/code/getting_started/app/assets/stylesheets/application.css
new file mode 100644
index 0000000000..fc25b5723f
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/stylesheets/application.css
@@ -0,0 +1,7 @@
+/*
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
+ *= require_self
+ *= require_tree .
+*/ \ No newline at end of file
diff --git a/railties/guides/code/getting_started/app/assets/stylesheets/comments.css.scss b/railties/guides/code/getting_started/app/assets/stylesheets/comments.css.scss
new file mode 100644
index 0000000000..e730912783
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/stylesheets/comments.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the Comments controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/railties/guides/code/getting_started/app/assets/stylesheets/home.css.scss b/railties/guides/code/getting_started/app/assets/stylesheets/home.css.scss
new file mode 100644
index 0000000000..f0ddc6846a
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/stylesheets/home.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the home controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/railties/guides/code/getting_started/app/assets/stylesheets/posts.css.scss b/railties/guides/code/getting_started/app/assets/stylesheets/posts.css.scss
new file mode 100644
index 0000000000..ed4dfd10f2
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/stylesheets/posts.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the Posts controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/railties/guides/code/getting_started/app/assets/stylesheets/scaffolds.css.scss b/railties/guides/code/getting_started/app/assets/stylesheets/scaffolds.css.scss
new file mode 100644
index 0000000000..05188f08ed
--- /dev/null
+++ b/railties/guides/code/getting_started/app/assets/stylesheets/scaffolds.css.scss
@@ -0,0 +1,56 @@
+body {
+ background-color: #fff;
+ color: #333;
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px; }
+
+p, ol, ul, td {
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px; }
+
+pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px; }
+
+a {
+ color: #000;
+ &:visited {
+ color: #666; }
+ &:hover {
+ color: #fff;
+ background-color: #000; } }
+
+div {
+ &.field, &.actions {
+ margin-bottom: 10px; } }
+
+#notice {
+ color: green; }
+
+.field_with_errors {
+ padding: 2px;
+ background-color: red;
+ display: table; }
+
+#error_explanation {
+ width: 450px;
+ border: 2px solid red;
+ padding: 7px;
+ padding-bottom: 0;
+ margin-bottom: 20px;
+ background-color: #f0f0f0;
+ h2 {
+ text-align: left;
+ font-weight: bold;
+ padding: 5px 5px 5px 15px;
+ font-size: 12px;
+ margin: -7px;
+ margin-bottom: 0px;
+ background-color: #c00;
+ color: #fff; }
+ ul li {
+ font-size: 12px;
+ list-style: square; } }
diff --git a/railties/guides/code/getting_started/app/controllers/application_controller.rb b/railties/guides/code/getting_started/app/controllers/application_controller.rb
new file mode 100644
index 0000000000..e8065d9505
--- /dev/null
+++ b/railties/guides/code/getting_started/app/controllers/application_controller.rb
@@ -0,0 +1,3 @@
+class ApplicationController < ActionController::Base
+ protect_from_forgery
+end
diff --git a/railties/guides/code/getting_started/app/controllers/comments_controller.rb b/railties/guides/code/getting_started/app/controllers/comments_controller.rb
new file mode 100644
index 0000000000..7447fd078b
--- /dev/null
+++ b/railties/guides/code/getting_started/app/controllers/comments_controller.rb
@@ -0,0 +1,16 @@
+class CommentsController < ApplicationController
+ http_basic_authenticate_with :name => "dhh", :password => "secret", :only => :destroy
+ def create
+ @post = Post.find(params[:post_id])
+ @comment = @post.comments.create(params[:comment])
+ redirect_to post_path(@post)
+ end
+
+ def destroy
+ @post = Post.find(params[:post_id])
+ @comment = @post.comments.find(params[:id])
+ @comment.destroy
+ redirect_to post_path(@post)
+ end
+
+end
diff --git a/railties/guides/code/getting_started/app/controllers/home_controller.rb b/railties/guides/code/getting_started/app/controllers/home_controller.rb
new file mode 100644
index 0000000000..6cc31c1ca3
--- /dev/null
+++ b/railties/guides/code/getting_started/app/controllers/home_controller.rb
@@ -0,0 +1,5 @@
+class HomeController < ApplicationController
+ def index
+ end
+
+end
diff --git a/railties/guides/code/getting_started/app/controllers/posts_controller.rb b/railties/guides/code/getting_started/app/controllers/posts_controller.rb
new file mode 100644
index 0000000000..7e903c984c
--- /dev/null
+++ b/railties/guides/code/getting_started/app/controllers/posts_controller.rb
@@ -0,0 +1,84 @@
+class PostsController < ApplicationController
+ http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
+ # GET /posts
+ # GET /posts.json
+ def index
+ @posts = Post.all
+
+ respond_to do |format|
+ format.html # index.html.erb
+ format.json { render json: @posts }
+ end
+ end
+
+ # GET /posts/1
+ # GET /posts/1.json
+ def show
+ @post = Post.find(params[:id])
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.json { render json: @post }
+ end
+ end
+
+ # GET /posts/new
+ # GET /posts/new.json
+ def new
+ @post = Post.new
+
+ respond_to do |format|
+ format.html # new.html.erb
+ format.json { render json: @post }
+ end
+ end
+
+ # GET /posts/1/edit
+ def edit
+ @post = Post.find(params[:id])
+ end
+
+ # POST /posts
+ # POST /posts.json
+ def create
+ @post = Post.new(params[:post])
+
+ respond_to do |format|
+ if @post.save
+ format.html { redirect_to @post, notice: 'Post was successfully created.' }
+ format.json { render json: @post, status: :created, location: @post }
+ else
+ format.html { render action: "new" }
+ format.json { render json: @post.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /posts/1
+ # PUT /posts/1.json
+ def update
+ @post = Post.find(params[:id])
+
+ respond_to do |format|
+ if @post.update_attributes(params[:post])
+ format.html { redirect_to @post, notice: 'Post was successfully updated.' }
+ format.json { head :ok }
+ else
+ format.html { render action: "edit" }
+ format.json { render json: @post.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /posts/1
+ # DELETE /posts/1.json
+ def destroy
+ @post = Post.find(params[:id])
+ @post.destroy
+
+ respond_to do |format|
+ format.html { redirect_to posts_url }
+ format.json { head :ok }
+ end
+ end
+end
diff --git a/railties/guides/code/getting_started/app/helpers/application_helper.rb b/railties/guides/code/getting_started/app/helpers/application_helper.rb
new file mode 100644
index 0000000000..de6be7945c
--- /dev/null
+++ b/railties/guides/code/getting_started/app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
diff --git a/railties/guides/code/getting_started/app/helpers/comments_helper.rb b/railties/guides/code/getting_started/app/helpers/comments_helper.rb
new file mode 100644
index 0000000000..0ec9ca5f2d
--- /dev/null
+++ b/railties/guides/code/getting_started/app/helpers/comments_helper.rb
@@ -0,0 +1,2 @@
+module CommentsHelper
+end
diff --git a/railties/guides/code/getting_started/app/helpers/home_helper.rb b/railties/guides/code/getting_started/app/helpers/home_helper.rb
new file mode 100644
index 0000000000..23de56ac60
--- /dev/null
+++ b/railties/guides/code/getting_started/app/helpers/home_helper.rb
@@ -0,0 +1,2 @@
+module HomeHelper
+end
diff --git a/railties/guides/code/getting_started/app/helpers/posts_helper.rb b/railties/guides/code/getting_started/app/helpers/posts_helper.rb
new file mode 100644
index 0000000000..b6e8e67894
--- /dev/null
+++ b/railties/guides/code/getting_started/app/helpers/posts_helper.rb
@@ -0,0 +1,5 @@
+module PostsHelper
+ def join_tags(post)
+ post.tags.map { |t| t.name }.join(", ")
+ end
+end
diff --git a/railties/guides/code/getting_started/app/mailers/.gitkeep b/railties/guides/code/getting_started/app/mailers/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/app/mailers/.gitkeep
diff --git a/railties/guides/code/getting_started/app/models/.gitkeep b/railties/guides/code/getting_started/app/models/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/app/models/.gitkeep
diff --git a/railties/guides/code/getting_started/app/models/comment.rb b/railties/guides/code/getting_started/app/models/comment.rb
new file mode 100644
index 0000000000..4e76c5b5b0
--- /dev/null
+++ b/railties/guides/code/getting_started/app/models/comment.rb
@@ -0,0 +1,3 @@
+class Comment < ActiveRecord::Base
+ belongs_to :post
+end
diff --git a/railties/guides/code/getting_started/app/models/post.rb b/railties/guides/code/getting_started/app/models/post.rb
new file mode 100644
index 0000000000..61c2b5ae44
--- /dev/null
+++ b/railties/guides/code/getting_started/app/models/post.rb
@@ -0,0 +1,11 @@
+class Post < ActiveRecord::Base
+ validates :name, :presence => true
+ validates :title, :presence => true,
+ :length => { :minimum => 5 }
+
+ has_many :comments, :dependent => :destroy
+ has_many :tags
+
+ accepts_nested_attributes_for :tags, :allow_destroy => :true,
+ :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
+end
diff --git a/railties/guides/code/getting_started/app/models/tag.rb b/railties/guides/code/getting_started/app/models/tag.rb
new file mode 100644
index 0000000000..30992e8ba9
--- /dev/null
+++ b/railties/guides/code/getting_started/app/models/tag.rb
@@ -0,0 +1,3 @@
+class Tag < ActiveRecord::Base
+ belongs_to :post
+end
diff --git a/railties/guides/code/getting_started/app/views/comments/_comment.html.erb b/railties/guides/code/getting_started/app/views/comments/_comment.html.erb
new file mode 100644
index 0000000000..4c3fbf26cd
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/comments/_comment.html.erb
@@ -0,0 +1,15 @@
+<p>
+ <b>Commenter:</b>
+ <%= comment.commenter %>
+</p>
+
+<p>
+ <b>Comment:</b>
+ <%= comment.body %>
+</p>
+
+<p>
+ <%= link_to 'Destroy Comment', [comment.post, comment],
+ :confirm => 'Are you sure?',
+ :method => :delete %>
+</p>
diff --git a/railties/guides/code/getting_started/app/views/comments/_form.html.erb b/railties/guides/code/getting_started/app/views/comments/_form.html.erb
new file mode 100644
index 0000000000..d15bdd6b59
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/comments/_form.html.erb
@@ -0,0 +1,13 @@
+<%= form_for([@post, @post.comments.build]) do |f| %>
+ <div class="field">
+ <%= f.label :commenter %><br />
+ <%= f.text_field :commenter %>
+ </div>
+ <div class="field">
+ <%= f.label :body %><br />
+ <%= f.text_area :body %>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
+<% end %>
diff --git a/railties/guides/code/getting_started/app/views/home/index.html.erb b/railties/guides/code/getting_started/app/views/home/index.html.erb
new file mode 100644
index 0000000000..bb4f3dcd1f
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/home/index.html.erb
@@ -0,0 +1,2 @@
+<h1>Hello, Rails!</h1>
+<%= link_to "My Blog", posts_path %>
diff --git a/railties/guides/code/getting_started/app/views/home/index.html.erb~ b/railties/guides/code/getting_started/app/views/home/index.html.erb~
new file mode 100644
index 0000000000..2085730c72
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/home/index.html.erb~
@@ -0,0 +1,2 @@
+<h1>Home#index</h1>
+<p>Find me in app/views/home/index.html.erb</p>
diff --git a/railties/guides/code/getting_started/app/views/layouts/application.html.erb b/railties/guides/code/getting_started/app/views/layouts/application.html.erb
new file mode 100644
index 0000000000..1e1e4b9a99
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/layouts/application.html.erb
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Blog</title>
+ <%= stylesheet_link_tag "application" %>
+ <%= javascript_include_tag "application" %>
+ <%= csrf_meta_tags %>
+</head>
+<body style="background: #EEEEEE;">
+
+<%= yield %>
+
+</body>
+</html>
diff --git a/railties/guides/code/getting_started/app/views/posts/_form.html.erb b/railties/guides/code/getting_started/app/views/posts/_form.html.erb
new file mode 100644
index 0000000000..e27da7f413
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/posts/_form.html.erb
@@ -0,0 +1,32 @@
+<% @post.tags.build %>
+<%= form_for(@post) do |post_form| %>
+ <% if @post.errors.any? %>
+ <div id="errorExplanation">
+ <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
+ <ul>
+ <% @post.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <div class="field">
+ <%= post_form.label :name %><br />
+ <%= post_form.text_field :name %>
+ </div>
+ <div class="field">
+ <%= post_form.label :title %><br />
+ <%= post_form.text_field :title %>
+ </div>
+ <div class="field">
+ <%= post_form.label :content %><br />
+ <%= post_form.text_area :content %>
+ </div>
+ <h2>Tags</h2>
+ <%= render :partial => 'tags/form',
+ :locals => {:form => post_form} %>
+ <div class="actions">
+ <%= post_form.submit %>
+ </div>
+<% end %>
diff --git a/railties/guides/code/getting_started/app/views/posts/edit.html.erb b/railties/guides/code/getting_started/app/views/posts/edit.html.erb
new file mode 100644
index 0000000000..720580236b
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/posts/edit.html.erb
@@ -0,0 +1,6 @@
+<h1>Editing post</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Show', @post %> |
+<%= link_to 'Back', posts_path %>
diff --git a/railties/guides/code/getting_started/app/views/posts/index.html.erb b/railties/guides/code/getting_started/app/views/posts/index.html.erb
new file mode 100644
index 0000000000..45dee1b25f
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/posts/index.html.erb
@@ -0,0 +1,27 @@
+<h1>Listing posts</h1>
+
+<table>
+ <tr>
+ <th>Name</th>
+ <th>Title</th>
+ <th>Content</th>
+ <th></th>
+ <th></th>
+ <th></th>
+ </tr>
+
+<% @posts.each do |post| %>
+ <tr>
+ <td><%= post.name %></td>
+ <td><%= post.title %></td>
+ <td><%= post.content %></td>
+ <td><%= link_to 'Show', post %></td>
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
+ <td><%= link_to 'Destroy', post, confirm: 'Are you sure?', method: :delete %></td>
+ </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New Post', new_post_path %>
diff --git a/railties/guides/code/getting_started/app/views/posts/new.html.erb b/railties/guides/code/getting_started/app/views/posts/new.html.erb
new file mode 100644
index 0000000000..36ad7421f9
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/posts/new.html.erb
@@ -0,0 +1,5 @@
+<h1>New post</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Back', posts_path %>
diff --git a/railties/guides/code/getting_started/app/views/posts/show.html.erb b/railties/guides/code/getting_started/app/views/posts/show.html.erb
new file mode 100644
index 0000000000..da78a9527b
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/posts/show.html.erb
@@ -0,0 +1,31 @@
+<p class="notice"><%= notice %></p>
+
+<p>
+ <b>Name:</b>
+ <%= @post.name %>
+</p>
+
+<p>
+ <b>Title:</b>
+ <%= @post.title %>
+</p>
+
+<p>
+ <b>Content:</b>
+ <%= @post.content %>
+</p>
+
+<p>
+ <b>Tags:</b>
+ <%= join_tags(@post) %>
+</p>
+
+<h2>Comments</h2>
+<%= render @post.comments %>
+
+<h2>Add a comment:</h2>
+<%= render "comments/form" %>
+
+
+<%= link_to 'Edit Post', edit_post_path(@post) %> |
+<%= link_to 'Back to Posts', posts_path %> |
diff --git a/railties/guides/code/getting_started/app/views/tags/_form.html.erb b/railties/guides/code/getting_started/app/views/tags/_form.html.erb
new file mode 100644
index 0000000000..7e424b0e20
--- /dev/null
+++ b/railties/guides/code/getting_started/app/views/tags/_form.html.erb
@@ -0,0 +1,12 @@
+<%= form.fields_for :tags do |tag_form| %>
+ <div class="field">
+ <%= tag_form.label :name, 'Tag:' %>
+ <%= tag_form.text_field :name %>
+ </div>
+ <% unless tag_form.object.nil? || tag_form.object.new_record? %>
+ <div class="field">
+ <%= tag_form.label :_destroy, 'Remove:' %>
+ <%= tag_form.check_box :_destroy %>
+ </div>
+ <% end %>
+<% end %>
diff --git a/railties/guides/code/getting_started/config.ru b/railties/guides/code/getting_started/config.ru
new file mode 100644
index 0000000000..ddf869e921
--- /dev/null
+++ b/railties/guides/code/getting_started/config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Blog::Application
diff --git a/railties/guides/code/getting_started/config/application.rb b/railties/guides/code/getting_started/config/application.rb
new file mode 100644
index 0000000000..e914b5a80e
--- /dev/null
+++ b/railties/guides/code/getting_started/config/application.rb
@@ -0,0 +1,48 @@
+require File.expand_path('../boot', __FILE__)
+
+require 'rails/all'
+
+if defined?(Bundler)
+ # If you precompile assets before deploying to production, use this line
+ Bundler.require *Rails.groups(:assets => %w(development test))
+ # If you want your assets lazily compiled in production, use this line
+ # Bundler.require(:default, :assets, Rails.env)
+end
+
+module Blog
+ class Application < Rails::Application
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+
+ # Custom directories with classes and modules you want to be autoloadable.
+ # config.autoload_paths += %W(#{config.root}/extras)
+
+ # Only load the plugins named here, in the order given (default is alphabetical).
+ # :all can be used as a placeholder for all plugins not explicitly named.
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+
+ # Activate observers that should always be running.
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+
+ # 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. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # 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}').to_s]
+ # config.i18n.default_locale = :de
+
+ # Configure the default encoding used in templates for Ruby 1.9.
+ config.encoding = "utf-8"
+
+ # Configure sensitive parameters which will be filtered from the log file.
+ config.filter_parameters += [:password]
+
+ # Enable the asset pipeline
+ config.assets.enabled = true
+
+ # Version of your assets, change this if you want to expire all your assets
+ config.assets.version = '1.0'
+ end
+end
diff --git a/railties/guides/code/getting_started/config/boot.rb b/railties/guides/code/getting_started/config/boot.rb
new file mode 100644
index 0000000000..4489e58688
--- /dev/null
+++ b/railties/guides/code/getting_started/config/boot.rb
@@ -0,0 +1,6 @@
+require 'rubygems'
+
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+
+require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
diff --git a/railties/guides/code/getting_started/config/database.yml b/railties/guides/code/getting_started/config/database.yml
new file mode 100644
index 0000000000..51a4dd459d
--- /dev/null
+++ b/railties/guides/code/getting_started/config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+development:
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ database: db/test.sqlite3
+ pool: 5
+ timeout: 5000
+
+production:
+ adapter: sqlite3
+ database: db/production.sqlite3
+ pool: 5
+ timeout: 5000
diff --git a/railties/guides/code/getting_started/config/environment.rb b/railties/guides/code/getting_started/config/environment.rb
new file mode 100644
index 0000000000..8f728b7ce7
--- /dev/null
+++ b/railties/guides/code/getting_started/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the rails application
+require File.expand_path('../application', __FILE__)
+
+# Initialize the rails application
+Blog::Application.initialize!
diff --git a/railties/guides/code/getting_started/config/environments/development.rb b/railties/guides/code/getting_started/config/environments/development.rb
new file mode 100644
index 0000000000..89932bf19b
--- /dev/null
+++ b/railties/guides/code/getting_started/config/environments/development.rb
@@ -0,0 +1,30 @@
+Blog::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Don't care if the mailer can't send
+ config.action_mailer.raise_delivery_errors = false
+
+ # Print deprecation notices to the Rails logger
+ config.active_support.deprecation = :log
+
+ # Only use best-standards-support built into browsers
+ config.action_dispatch.best_standards_support = :builtin
+
+ # Do not compress assets
+ config.assets.compress = false
+
+ # Expands the lines which load the assets
+ config.assets.debug = true
+end
diff --git a/railties/guides/code/getting_started/config/environments/production.rb b/railties/guides/code/getting_started/config/environments/production.rb
new file mode 100644
index 0000000000..6ab63d30a6
--- /dev/null
+++ b/railties/guides/code/getting_started/config/environments/production.rb
@@ -0,0 +1,60 @@
+Blog::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # Code is not reloaded between requests
+ config.cache_classes = true
+
+ # Full error reports are disabled and caching is turned on
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Disable Rails's static asset server (Apache or nginx will already do this)
+ config.serve_static_assets = false
+
+ # Compress JavaScripts and CSS
+ config.assets.compress = true
+
+ # Don't fallback to assets pipeline if a precompiled asset is missed
+ config.assets.compile = false
+
+ # Generate digests for assets URLs
+ config.assets.digest = true
+
+ # Defaults to Rails.root.join("public/assets")
+ # config.assets.manifest = YOUR_PATH
+
+ # Specifies the header that your server uses for sending files
+ # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # See everything in the log (default is :info)
+ # config.log_level = :debug
+
+ # Use a different logger for distributed setups
+ # config.logger = SyslogLogger.new
+
+ # Use a different cache store in production
+ # config.cache_store = :mem_cache_store
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server
+ # config.action_controller.asset_host = "http://assets.example.com"
+
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+ # config.assets.precompile += %w( search.js )
+
+ # Disable delivery errors, bad email addresses will be ignored
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable threaded mode
+ # config.threadsafe!
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation can not be found)
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners
+ config.active_support.deprecation = :notify
+end
diff --git a/railties/guides/code/getting_started/config/environments/test.rb b/railties/guides/code/getting_started/config/environments/test.rb
new file mode 100644
index 0000000000..833241ace3
--- /dev/null
+++ b/railties/guides/code/getting_started/config/environments/test.rb
@@ -0,0 +1,42 @@
+Blog::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Configure static asset server for tests with Cache-Control for performance
+ config.serve_static_assets = true
+ config.static_cache_control = "public, max-age=3600"
+
+ # Log error messages when you accidentally call methods on nil
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment
+ config.action_controller.allow_forgery_protection = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Use SQL instead of Active Record's schema dumper when creating the test database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types
+ # config.active_record.schema_format = :sql
+
+ # Print deprecation notices to the stderr
+ config.active_support.deprecation = :stderr
+
+ # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
+ config.assets.allow_debugging = true
+end
diff --git a/railties/guides/code/getting_started/config/initializers/backtrace_silencers.rb b/railties/guides/code/getting_started/config/initializers/backtrace_silencers.rb
new file mode 100644
index 0000000000..59385cdf37
--- /dev/null
+++ b/railties/guides/code/getting_started/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/railties/guides/code/getting_started/config/initializers/inflections.rb b/railties/guides/code/getting_started/config/initializers/inflections.rb
new file mode 100644
index 0000000000..9e8b0131f8
--- /dev/null
+++ b/railties/guides/code/getting_started/config/initializers/inflections.rb
@@ -0,0 +1,10 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format
+# (all these examples are active by default):
+# ActiveSupport::Inflector.inflections do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
diff --git a/railties/guides/code/getting_started/config/initializers/mime_types.rb b/railties/guides/code/getting_started/config/initializers/mime_types.rb
new file mode 100644
index 0000000000..72aca7e441
--- /dev/null
+++ b/railties/guides/code/getting_started/config/initializers/mime_types.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
+# Mime::Type.register_alias "text/html", :iphone
diff --git a/railties/guides/code/getting_started/config/initializers/secret_token.rb b/railties/guides/code/getting_started/config/initializers/secret_token.rb
new file mode 100644
index 0000000000..b0c8ee23c1
--- /dev/null
+++ b/railties/guides/code/getting_started/config/initializers/secret_token.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+Blog::Application.config.secret_token = '685a9bf865b728c6549a191c90851c1b5ec41ecb60b9e94ad79dd3f824749798aa7b5e94431901960bee57809db0947b481570f7f13376b7ca190fa28099c459'
diff --git a/railties/guides/code/getting_started/config/initializers/session_store.rb b/railties/guides/code/getting_started/config/initializers/session_store.rb
new file mode 100644
index 0000000000..1a67af58b5
--- /dev/null
+++ b/railties/guides/code/getting_started/config/initializers/session_store.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+Blog::Application.config.session_store :cookie_store, key: '_blog_session'
+
+# Use the database for sessions instead of the cookie-based default,
+# which shouldn't be used to store highly confidential information
+# (create the session table with "rails generate session_migration")
+# Blog::Application.config.session_store :active_record_store
diff --git a/railties/guides/code/getting_started/config/initializers/wrap_parameters.rb b/railties/guides/code/getting_started/config/initializers/wrap_parameters.rb
new file mode 100644
index 0000000000..999df20181
--- /dev/null
+++ b/railties/guides/code/getting_started/config/initializers/wrap_parameters.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+#
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
+
+# Disable root element in JSON by default.
+ActiveSupport.on_load(:active_record) do
+ self.include_root_in_json = false
+end
diff --git a/railties/guides/code/getting_started/config/locales/en.yml b/railties/guides/code/getting_started/config/locales/en.yml
new file mode 100644
index 0000000000..179c14ca52
--- /dev/null
+++ b/railties/guides/code/getting_started/config/locales/en.yml
@@ -0,0 +1,5 @@
+# Sample localization file for English. Add more files in this directory for other locales.
+# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+
+en:
+ hello: "Hello world"
diff --git a/railties/guides/code/getting_started/config/routes.rb b/railties/guides/code/getting_started/config/routes.rb
new file mode 100644
index 0000000000..31f0d210db
--- /dev/null
+++ b/railties/guides/code/getting_started/config/routes.rb
@@ -0,0 +1,64 @@
+Blog::Application.routes.draw do
+ resources :posts do
+ resources :comments
+ end
+
+ get "home/index"
+
+ # The priority is based upon order of creation:
+ # first created -> highest priority.
+
+ # Sample of regular route:
+ # match 'products/:id' => 'catalog#view'
+ # Keep in mind you can assign values other than :controller and :action
+
+ # Sample of named route:
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
+ # This route can be invoked with purchase_url(:id => product.id)
+
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
+ # resources :products
+
+ # Sample resource route with options:
+ # resources :products do
+ # member do
+ # get 'short'
+ # post 'toggle'
+ # end
+ #
+ # collection do
+ # get 'sold'
+ # end
+ # end
+
+ # Sample resource route with sub-resources:
+ # resources :products do
+ # resources :comments, :sales
+ # resource :seller
+ # end
+
+ # Sample resource route with more complex sub-resources
+ # resources :products do
+ # resources :comments
+ # resources :sales do
+ # get 'recent', :on => :collection
+ # end
+ # end
+
+ # Sample resource route within a namespace:
+ # namespace :admin do
+ # # Directs /admin/products/* to Admin::ProductsController
+ # # (app/controllers/admin/products_controller.rb)
+ # resources :products
+ # end
+
+ # You can have the root of your site routed with "root"
+ # just remember to delete public/index.html.
+ root :to => "home#index"
+
+ # See how all your routes lay out with "rake routes"
+
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
+ # Note: This route will make all actions in every controller accessible via GET requests.
+ # match ':controller(/:action(/:id(.:format)))'
+end
diff --git a/railties/guides/code/getting_started/config/routes.rb~ b/railties/guides/code/getting_started/config/routes.rb~
new file mode 100644
index 0000000000..3d9628b55e
--- /dev/null
+++ b/railties/guides/code/getting_started/config/routes.rb~
@@ -0,0 +1,60 @@
+Blog::Application.routes.draw do
+ get "home/index"
+
+ # The priority is based upon order of creation:
+ # first created -> highest priority.
+
+ # Sample of regular route:
+ # match 'products/:id' => 'catalog#view'
+ # Keep in mind you can assign values other than :controller and :action
+
+ # Sample of named route:
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
+ # This route can be invoked with purchase_url(:id => product.id)
+
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
+ # resources :products
+
+ # Sample resource route with options:
+ # resources :products do
+ # member do
+ # get 'short'
+ # post 'toggle'
+ # end
+ #
+ # collection do
+ # get 'sold'
+ # end
+ # end
+
+ # Sample resource route with sub-resources:
+ # resources :products do
+ # resources :comments, :sales
+ # resource :seller
+ # end
+
+ # Sample resource route with more complex sub-resources
+ # resources :products do
+ # resources :comments
+ # resources :sales do
+ # get 'recent', :on => :collection
+ # end
+ # end
+
+ # Sample resource route within a namespace:
+ # namespace :admin do
+ # # Directs /admin/products/* to Admin::ProductsController
+ # # (app/controllers/admin/products_controller.rb)
+ # resources :products
+ # end
+
+ # You can have the root of your site routed with "root"
+ # just remember to delete public/index.html.
+ # root :to => 'welcome#index'
+
+ # See how all your routes lay out with "rake routes"
+
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
+ # Note: This route will make all actions in every controller accessible via GET requests.
+ # match ':controller(/:action(/:id(.:format)))'
+end
diff --git a/railties/guides/code/getting_started/db/migrate/20110901012504_create_posts.rb b/railties/guides/code/getting_started/db/migrate/20110901012504_create_posts.rb
new file mode 100644
index 0000000000..d45a961523
--- /dev/null
+++ b/railties/guides/code/getting_started/db/migrate/20110901012504_create_posts.rb
@@ -0,0 +1,11 @@
+class CreatePosts < ActiveRecord::Migration
+ def change
+ create_table :posts do |t|
+ t.string :name
+ t.string :title
+ t.text :content
+
+ t.timestamps
+ end
+ end
+end
diff --git a/railties/guides/code/getting_started/db/migrate/20110901012815_create_comments.rb b/railties/guides/code/getting_started/db/migrate/20110901012815_create_comments.rb
new file mode 100644
index 0000000000..adda8078c1
--- /dev/null
+++ b/railties/guides/code/getting_started/db/migrate/20110901012815_create_comments.rb
@@ -0,0 +1,12 @@
+class CreateComments < ActiveRecord::Migration
+ def change
+ create_table :comments do |t|
+ t.string :commenter
+ t.text :body
+ t.references :post
+
+ t.timestamps
+ end
+ add_index :comments, :post_id
+ end
+end
diff --git a/railties/guides/code/getting_started/db/migrate/20110901013701_create_tags.rb b/railties/guides/code/getting_started/db/migrate/20110901013701_create_tags.rb
new file mode 100644
index 0000000000..cf95b1c3d0
--- /dev/null
+++ b/railties/guides/code/getting_started/db/migrate/20110901013701_create_tags.rb
@@ -0,0 +1,11 @@
+class CreateTags < ActiveRecord::Migration
+ def change
+ create_table :tags do |t|
+ t.string :name
+ t.references :post
+
+ t.timestamps
+ end
+ add_index :tags, :post_id
+ end
+end
diff --git a/railties/guides/code/getting_started/db/schema.rb b/railties/guides/code/getting_started/db/schema.rb
new file mode 100644
index 0000000000..9db4fbe4b6
--- /dev/null
+++ b/railties/guides/code/getting_started/db/schema.rb
@@ -0,0 +1,43 @@
+# encoding: UTF-8
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 20110901013701) do
+
+ create_table "comments", :force => true do |t|
+ t.string "commenter"
+ t.text "body"
+ t.integer "post_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "comments", ["post_id"], :name => "index_comments_on_post_id"
+
+ create_table "posts", :force => true do |t|
+ t.string "name"
+ t.string "title"
+ t.text "content"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "tags", :force => true do |t|
+ t.string "name"
+ t.integer "post_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "tags", ["post_id"], :name => "index_tags_on_post_id"
+
+end
diff --git a/railties/guides/code/getting_started/db/seeds.rb b/railties/guides/code/getting_started/db/seeds.rb
new file mode 100644
index 0000000000..4edb1e857e
--- /dev/null
+++ b/railties/guides/code/getting_started/db/seeds.rb
@@ -0,0 +1,7 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
+# Mayor.create(name: 'Emanuel', city: cities.first)
diff --git a/railties/guides/code/getting_started/doc/README_FOR_APP b/railties/guides/code/getting_started/doc/README_FOR_APP
new file mode 100644
index 0000000000..fe41f5cc24
--- /dev/null
+++ b/railties/guides/code/getting_started/doc/README_FOR_APP
@@ -0,0 +1,2 @@
+Use this README file to introduce your application and point to useful places in the API for learning more.
+Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
diff --git a/railties/guides/code/getting_started/lib/assets/.gitkeep b/railties/guides/code/getting_started/lib/assets/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/lib/assets/.gitkeep
diff --git a/railties/guides/code/getting_started/lib/tasks/.gitkeep b/railties/guides/code/getting_started/lib/tasks/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/lib/tasks/.gitkeep
diff --git a/railties/guides/code/getting_started/public/404.html b/railties/guides/code/getting_started/public/404.html
new file mode 100644
index 0000000000..9a48320a5f
--- /dev/null
+++ b/railties/guides/code/getting_started/public/404.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The page you were looking for doesn't exist (404)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/404.html -->
+ <div class="dialog">
+ <h1>The page you were looking for doesn't exist.</h1>
+ <p>You may have mistyped the address or the page may have moved.</p>
+ </div>
+</body>
+</html>
diff --git a/railties/guides/code/getting_started/public/422.html b/railties/guides/code/getting_started/public/422.html
new file mode 100644
index 0000000000..83660ab187
--- /dev/null
+++ b/railties/guides/code/getting_started/public/422.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The change you wanted was rejected (422)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/422.html -->
+ <div class="dialog">
+ <h1>The change you wanted was rejected.</h1>
+ <p>Maybe you tried to change something you didn't have access to.</p>
+ </div>
+</body>
+</html>
diff --git a/railties/guides/code/getting_started/public/500.html b/railties/guides/code/getting_started/public/500.html
new file mode 100644
index 0000000000..b80307fc16
--- /dev/null
+++ b/railties/guides/code/getting_started/public/500.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>We're sorry, but something went wrong (500)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/500.html -->
+ <div class="dialog">
+ <h1>We're sorry, but something went wrong.</h1>
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
+ </div>
+</body>
+</html>
diff --git a/railties/guides/code/getting_started/public/favicon.ico b/railties/guides/code/getting_started/public/favicon.ico
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/public/favicon.ico
diff --git a/railties/guides/code/getting_started/public/robots.txt b/railties/guides/code/getting_started/public/robots.txt
new file mode 100644
index 0000000000..085187fa58
--- /dev/null
+++ b/railties/guides/code/getting_started/public/robots.txt
@@ -0,0 +1,5 @@
+# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
+#
+# To ban all spiders from the entire site uncomment the next two lines:
+# User-Agent: *
+# Disallow: /
diff --git a/railties/guides/code/getting_started/script/rails b/railties/guides/code/getting_started/script/rails
new file mode 100755
index 0000000000..f8da2cffd4
--- /dev/null
+++ b/railties/guides/code/getting_started/script/rails
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
+
+APP_PATH = File.expand_path('../../config/application', __FILE__)
+require File.expand_path('../../config/boot', __FILE__)
+require 'rails/commands'
diff --git a/railties/guides/code/getting_started/test/fixtures/.gitkeep b/railties/guides/code/getting_started/test/fixtures/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/test/fixtures/.gitkeep
diff --git a/railties/guides/code/getting_started/test/fixtures/comments.yml b/railties/guides/code/getting_started/test/fixtures/comments.yml
new file mode 100644
index 0000000000..d33da386bf
--- /dev/null
+++ b/railties/guides/code/getting_started/test/fixtures/comments.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+
+one:
+ commenter: MyString
+ body: MyText
+ post:
+
+two:
+ commenter: MyString
+ body: MyText
+ post:
diff --git a/railties/guides/code/getting_started/test/fixtures/posts.yml b/railties/guides/code/getting_started/test/fixtures/posts.yml
new file mode 100644
index 0000000000..8b0f75a33d
--- /dev/null
+++ b/railties/guides/code/getting_started/test/fixtures/posts.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+
+one:
+ name: MyString
+ title: MyString
+ content: MyText
+
+two:
+ name: MyString
+ title: MyString
+ content: MyText
diff --git a/railties/guides/code/getting_started/test/fixtures/tags.yml b/railties/guides/code/getting_started/test/fixtures/tags.yml
new file mode 100644
index 0000000000..8485668908
--- /dev/null
+++ b/railties/guides/code/getting_started/test/fixtures/tags.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+
+one:
+ name: MyString
+ post:
+
+two:
+ name: MyString
+ post:
diff --git a/railties/guides/code/getting_started/test/functional/.gitkeep b/railties/guides/code/getting_started/test/functional/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/test/functional/.gitkeep
diff --git a/railties/guides/code/getting_started/test/functional/comments_controller_test.rb b/railties/guides/code/getting_started/test/functional/comments_controller_test.rb
new file mode 100644
index 0000000000..2ec71b4ec5
--- /dev/null
+++ b/railties/guides/code/getting_started/test/functional/comments_controller_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class CommentsControllerTest < ActionController::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/railties/guides/code/getting_started/test/functional/home_controller_test.rb b/railties/guides/code/getting_started/test/functional/home_controller_test.rb
new file mode 100644
index 0000000000..0d9bb47c3e
--- /dev/null
+++ b/railties/guides/code/getting_started/test/functional/home_controller_test.rb
@@ -0,0 +1,9 @@
+require 'test_helper'
+
+class HomeControllerTest < ActionController::TestCase
+ test "should get index" do
+ get :index
+ assert_response :success
+ end
+
+end
diff --git a/railties/guides/code/getting_started/test/functional/posts_controller_test.rb b/railties/guides/code/getting_started/test/functional/posts_controller_test.rb
new file mode 100644
index 0000000000..b8f7b07820
--- /dev/null
+++ b/railties/guides/code/getting_started/test/functional/posts_controller_test.rb
@@ -0,0 +1,49 @@
+require 'test_helper'
+
+class PostsControllerTest < ActionController::TestCase
+ setup do
+ @post = posts(:one)
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ assert_not_nil assigns(:posts)
+ end
+
+ test "should get new" do
+ get :new
+ assert_response :success
+ end
+
+ test "should create post" do
+ assert_difference('Post.count') do
+ post :create, post: @post.attributes
+ end
+
+ assert_redirected_to post_path(assigns(:post))
+ end
+
+ test "should show post" do
+ get :show, id: @post.to_param
+ assert_response :success
+ end
+
+ test "should get edit" do
+ get :edit, id: @post.to_param
+ assert_response :success
+ end
+
+ test "should update post" do
+ put :update, id: @post.to_param, post: @post.attributes
+ assert_redirected_to post_path(assigns(:post))
+ end
+
+ test "should destroy post" do
+ assert_difference('Post.count', -1) do
+ delete :destroy, id: @post.to_param
+ end
+
+ assert_redirected_to posts_path
+ end
+end
diff --git a/railties/guides/code/getting_started/test/integration/.gitkeep b/railties/guides/code/getting_started/test/integration/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/test/integration/.gitkeep
diff --git a/railties/guides/code/getting_started/test/performance/browsing_test.rb b/railties/guides/code/getting_started/test/performance/browsing_test.rb
new file mode 100644
index 0000000000..3fea27b916
--- /dev/null
+++ b/railties/guides/code/getting_started/test/performance/browsing_test.rb
@@ -0,0 +1,12 @@
+require 'test_helper'
+require 'rails/performance_test_help'
+
+class BrowsingTest < ActionDispatch::PerformanceTest
+ # Refer to the documentation for all available options
+ # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]
+ # :output => 'tmp/performance', :formats => [:flat] }
+
+ def test_homepage
+ get '/'
+ end
+end
diff --git a/railties/guides/code/getting_started/test/test_helper.rb b/railties/guides/code/getting_started/test/test_helper.rb
new file mode 100644
index 0000000000..8bf1192ffe
--- /dev/null
+++ b/railties/guides/code/getting_started/test/test_helper.rb
@@ -0,0 +1,13 @@
+ENV["RAILS_ENV"] = "test"
+require File.expand_path('../../config/environment', __FILE__)
+require 'rails/test_help'
+
+class ActiveSupport::TestCase
+ # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
+ #
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
+ # -- they do not yet inherit this setting
+ fixtures :all
+
+ # Add more helper methods to be used by all tests here...
+end
diff --git a/railties/guides/code/getting_started/test/unit/.gitkeep b/railties/guides/code/getting_started/test/unit/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/test/unit/.gitkeep
diff --git a/railties/guides/code/getting_started/test/unit/comment_test.rb b/railties/guides/code/getting_started/test/unit/comment_test.rb
new file mode 100644
index 0000000000..b6d6131a96
--- /dev/null
+++ b/railties/guides/code/getting_started/test/unit/comment_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class CommentTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/railties/guides/code/getting_started/test/unit/helpers/comments_helper_test.rb b/railties/guides/code/getting_started/test/unit/helpers/comments_helper_test.rb
new file mode 100644
index 0000000000..2518c16bd5
--- /dev/null
+++ b/railties/guides/code/getting_started/test/unit/helpers/comments_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class CommentsHelperTest < ActionView::TestCase
+end
diff --git a/railties/guides/code/getting_started/test/unit/helpers/home_helper_test.rb b/railties/guides/code/getting_started/test/unit/helpers/home_helper_test.rb
new file mode 100644
index 0000000000..4740a18dac
--- /dev/null
+++ b/railties/guides/code/getting_started/test/unit/helpers/home_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class HomeHelperTest < ActionView::TestCase
+end
diff --git a/railties/guides/code/getting_started/test/unit/helpers/posts_helper_test.rb b/railties/guides/code/getting_started/test/unit/helpers/posts_helper_test.rb
new file mode 100644
index 0000000000..48549c2ea1
--- /dev/null
+++ b/railties/guides/code/getting_started/test/unit/helpers/posts_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class PostsHelperTest < ActionView::TestCase
+end
diff --git a/railties/guides/code/getting_started/test/unit/post_test.rb b/railties/guides/code/getting_started/test/unit/post_test.rb
new file mode 100644
index 0000000000..6d9d463a71
--- /dev/null
+++ b/railties/guides/code/getting_started/test/unit/post_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class PostTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/railties/guides/code/getting_started/test/unit/tag_test.rb b/railties/guides/code/getting_started/test/unit/tag_test.rb
new file mode 100644
index 0000000000..b8498a117c
--- /dev/null
+++ b/railties/guides/code/getting_started/test/unit/tag_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class TagTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/railties/guides/code/getting_started/vendor/assets/stylesheets/.gitkeep b/railties/guides/code/getting_started/vendor/assets/stylesheets/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/vendor/assets/stylesheets/.gitkeep
diff --git a/railties/guides/code/getting_started/vendor/plugins/.gitkeep b/railties/guides/code/getting_started/vendor/plugins/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/guides/code/getting_started/vendor/plugins/.gitkeep
diff --git a/railties/guides/source/3_1_release_notes.textile b/railties/guides/source/3_1_release_notes.textile
index 087926f98d..c4da87dc34 100644
--- a/railties/guides/source/3_1_release_notes.textile
+++ b/railties/guides/source/3_1_release_notes.textile
@@ -170,7 +170,7 @@ class PostsController < ActionController::Base
end
</ruby>
-You can restrict it to some actions by using +:only+ or +:except+. Please read the docs at "<tt>ActionController::Streaming</tt>":http://edgeapi.rubyonrails.org/classes/ActionController/Streaming.html for more information.
+You can restrict it to some actions by using +:only+ or +:except+. Please read the docs at "<tt>ActionController::Streaming</tt>":http://api.rubyonrails.org/classes/ActionController/Streaming.html for more information.
* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused.
@@ -245,7 +245,7 @@ class User < ActiveRecord::Base
has_one :account
end
-user.build_account{ |a| a.credit_limit => 100.0 }
+user.build_account{ |a| a.credit_limit = 100.0 }
</ruby>
* Added <tt>ActiveRecord::Base.attribute_names</tt> to return a list of attribute names. This will return an empty array if the model is abstract or the table does not exist.
@@ -271,7 +271,7 @@ Post.new(params[:post], :as => :admin)
* +ConnectionManagement+ middleware is changed to clean up the connection pool after the rack body has been flushed.
-* Added an +update_column+ method on Active Record. This new method updates a given attribute on an object, skipping validations and callbacks. It is recommended to use +update_attribute+ unless you are sure you do not want to execute any callback, including the modification of the +updated_at+ column. It should not be called on new records.
+* Added an +update_column+ method on Active Record. This new method updates a given attribute on an object, skipping validations and callbacks. It is recommended to use +update_attributes+ or +update_attribute+ unless you are sure you do not want to execute any callback, including the modification of the +updated_at+ column. It should not be called on new records.
* Associations with a +:through+ option can now use any association as the through or source association, including other associations which have a +:through+ option and +has_and_belongs_to_many+ associations.
@@ -315,7 +315,7 @@ end
<ruby>
class MyMigration < ActiveRecord::Migration
def change
- create_table(:horses) do
+ create_table(:horses) do |t|
t.column :content, :text
t.column :remind_at, :datetime
end
@@ -346,6 +346,7 @@ has_many :things, :conditions => proc { "foo = #{bar}" } # after
Inside the proc, +self+ is the object which is the owner of the association, unless you are eager loading the association, in which case +self+ is the class which the association is within.
You can have any "normal" conditions inside the proc, so the following will work too:
+
<ruby>
has_many :things, :conditions => proc { ["foo = ?", bar] }
</ruby>
@@ -353,6 +354,7 @@ has_many :things, :conditions => proc { ["foo = ?", bar] }
* Previously +:insert_sql+ and +:delete_sql+ on +has_and_belongs_to_many+ association allowed you to call 'record' to get the record being inserted or deleted. This is now passed as an argument to the proc.
* Added <tt>ActiveRecord::Base#has_secure_password</tt> (via <tt>ActiveModel::SecurePassword</tt>) to encapsulate dead-simple password usage with BCrypt encryption and salting.
+
<ruby>
# Schema: User(name:string, password_digest:string, password_salt:string)
class User < ActiveRecord::Base
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index 073e3bddcf..4e47712636 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -684,9 +684,11 @@ end
This will read and stream the file 4kB at the time, avoiding loading the entire file into memory at once. You can turn off streaming with the +:stream+ option or adjust the block size with the +:buffer_size+ option.
+If +:type+ is not specified, it will be guessed from the file extension specified in +:filename+. If the content type is not registered for the extension, <tt>application/octet-stream</tt> will be used.
+
WARNING: Be careful when using data coming from the client (params, cookies, etc.) to locate the file on disk, as this is a security risk that might allow someone to gain access to files they are not meant to see.
-TIP: It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack. Although if you do need the request to go through Rails for some reason, you can set the +:x_sendfile+ option to true, and Rails will let the web server handle sending the file to the user, freeing up the Rails process to do other things. Note that your web server needs to support the +X-Sendfile+ header for this to work.
+TIP: It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack.
h4. RESTful Downloads
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 142b9dba7e..67761645fa 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -467,7 +467,7 @@ The following configuration options are best made in one of the environment file
h4. Example Action Mailer Configuration
-An example would be adding the following to your appropriate <tt>config/environments/env.rb</tt> file:
+An example would be adding the following to your appropriate <tt>config/environments/$RAILS_ENV.rb</tt> file:
<ruby>
config.action_mailer.delivery_method = :sendmail
@@ -482,7 +482,7 @@ config.action_mailer.raise_delivery_errors = true
h4. Action Mailer Configuration for GMail
-As Action Mailer now uses the Mail gem, this becomes as simple as adding to your <tt>config/environments/env.rb</tt> file:
+As Action Mailer now uses the Mail gem, this becomes as simple as adding to your <tt>config/environments/$RAILS_ENV.rb</tt> file:
<ruby>
config.action_mailer.delivery_method = :smtp
@@ -514,8 +514,8 @@ class UserMailerTest < ActionMailer::TestCase
# Test the body of the sent email contains what we expect it to
assert_equal [user.email], email.to
assert_equal "Welcome to My Awesome Site", email.subject
- assert_match /<h1>Welcome to example.com, #{user.name}<\/h1>/, email.encoded
- assert_match /Welcome to example.com, #{user.name}/, email.encoded
+ assert_match(/<h1>Welcome to example.com, #{user.name}<\/h1>/, email.encoded)
+ assert_match(/Welcome to example.com, #{user.name}/, email.encoded)
end
end
</ruby>
@@ -524,4 +524,5 @@ In the test we send the email and store the returned object in the +email+ varia
h3. Changelog
+* September 1, 2011: Changed the lines that said <tt>config/environments/env.rb</tt> to <tt>config/environments/$RAILS_ENV.rb</tt>. People were mis-interpreting the filename to literally be env.rb. "Andy Leeper":http://mochaleaf.com
* September 30, 2010: Fixed typos and reformatted Action Mailer configuration table for better understanding. "Jaime Iniesta":http://jaimeiniesta.com
diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile
index 5a1e8b1247..edaaeb8543 100644
--- a/railties/guides/source/action_view_overview.textile
+++ b/railties/guides/source/action_view_overview.textile
@@ -454,6 +454,83 @@ input("post", "title") # =>
<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
</ruby>
+h4. RecordTagHelper
+
+This module provides methods for generating a container tag, such as a +<div>+, for your record. This is the recommended way of creating a container for render your Active Record object, as it adds an appropriate class and id attributes to that container. You can then refer to those containers easily by following the convention, instead of having to think about which class or id attribute you should use.
+
+h5. content_tag_for
+
+Renders a container tag that relates to your Active Record Object.
+
+For example, given +@post+ is the object of +Post+ class, you can do:
+
+<ruby>
+<%= content_tag_for(:tr, @post) do %>
+ <td><%= @post.title %></td>
+<% end %>
+</ruby>
+
+This will generate this HTML output:
+
+<html>
+<tr id="post_1234" class="post">
+ <td>Hello World!</td>
+</tr>
+</html>
+
+You can also supply HTML attributes as an additional option hash. For example:
+
+<ruby>
+<%= content_tag_for(:tr, @post, :class => "frontpage") do %>
+ <td><%= @post.title %></td>
+<% end %>
+</ruby>
+
+Will generate this HTML output:
+
+<html>
+<tr id="post_1234" class="post frontpage">
+ <td>Hello World!</td>
+</tr>
+</html>
+
+You can pass a collection of Active Record objects. This method will loop through your objects and create a container for each of them. For example, given +@posts+ is an array of two +Post+ objects:
+
+<ruby>
+<%= content_tag_for(:tr, @posts) do |post| %>
+ <td><%= post.title %></td>
+<% end %>
+</ruby>
+
+Will generate this HTML output:
+
+<html>
+<tr id="post_1234" class="post">
+ <td>Hello World!</td>
+</tr>
+<tr id="post_1235" class="post">
+ <td>Ruby on Rails Rocks!</td>
+</tr>
+</html>
+
+h5. div_for
+
+This is actually a convenient method which calls +content_tag_for+ internally with +:div+ as the tag name. You can pass either an Active Record object or a collection of objects. For example:
+
+<ruby>
+<%= div_for(@post, :class => "frontpage") do %>
+ <td><%= @post.title %></td>
+<% end %>
+</ruby>
+
+Will generate this HTML output:
+
+<html>
+<div id="post_1234" class="post frontpage">
+ <td>Hello World!</td>
+</div>
+</html>
+
h4. AssetTagHelper
This module provides methods for generating HTML that links views to assets such as images, JavaScript files, stylesheets, and feeds.
diff --git a/railties/guides/source/active_model_basics.textile b/railties/guides/source/active_model_basics.textile
index 0672669dc5..73df567579 100644
--- a/railties/guides/source/active_model_basics.textile
+++ b/railties/guides/source/active_model_basics.textile
@@ -163,12 +163,14 @@ person.first_name_changed? #=> true
</ruby>
Track what was the previous value of the attribute.
+
<ruby>
#attr_name_was accessor
person.first_name_was #=> "First Name"
</ruby>
Track both previous and current value of the changed attribute. Returns an array if changed else returns nil
+
<ruby>
#attr_name_change
person.first_name_change #=> ["First Name", "First Name 1"]
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 4e77a6e803..7a853db813 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -132,7 +132,7 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
<tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised.
-h5. +first!+
+h5(#first_1). +first!+
<tt>Model.first!</tt> finds the first record. For example:
@@ -149,7 +149,7 @@ SELECT * FROM clients LIMIT 1
<tt>Model.first!</tt> raises +RecordNotFound+ if no matching record is found.
-h5. +last!+
+h5(#last_1). +last!+
<tt>Model.last!</tt> finds the last record. For example:
@@ -1018,23 +1018,84 @@ If you want to find both by name and locked, you can chain these finders togethe
WARNING: Up to and including Rails 3.1, when the number of arguments passed to a dynamic finder method is lesser than the number of fields, say <tt>Client.find_by_name_and_locked("Ryan")</tt>, the behavior is to pass +nil+ as the missing argument. This is *unintentional* and this behavior will be changed in Rails 3.2 to throw an +ArgumentError+.
-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_first_name(params[:first_name])+. Using this will first perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_first_name("Ryan")+:
+h3. Find or build a new object
+
+It's common that you need to find a record or create it if it doesn't exist. You can do that with the +first_or_create+ and +first_or_create!+ methods.
+
+h4. +first_or_create+
+
+The +first_or_create+ method checks whether +first+ returns +nil+ or not. If it does return +nil+, then +create+ is called. This is very powerful when coupled with the +where+ method. Let's see an example.
+
+Suppose you want to find a client named 'Andy', and if there's none, create one and additionally set his +locked+ attribute to false. You can do so by running:
+
+<ruby>
+Client.where(:first_name => 'Andy').first_or_create(:locked => false)
+# => <Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
+</ruby>
+
+The SQL generated by this method looks like this:
<sql>
-SELECT * FROM clients WHERE (clients.first_name = 'Ryan') LIMIT 1
+SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1
BEGIN
-INSERT INTO clients (first_name, updated_at, created_at, orders_count, locked)
- VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0')
+INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 0, NULL, '2011-08-30 05:22:57')
COMMIT
</sql>
-+find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similarly to calling +new+ with the arguments you passed in. For example:
++first_or_create+ returns either the record that already existed or the new record. In our case, we didn't already have a client named Andy so the record was created an returned.
+
+The new record might not be saved to the database; that depends on whether validations passed or not (just like +create+).
+
+It's also worth noting that +first_or_create+ takes into account the arguments of the +where+ method. In the example above we didn't explicitly pass a +:first_name => 'Andy'+ argument to +first_or_create+. However, that was used when creating the new record because it was already passed before to the +where+ method.
+
+NOTE: On previous versions of Rails you could do a similar thing with the +find_or_create_by+ method. Following our example, you could also run something like +Client.find_or_create_by_first_name(:first_name => "Andy", :locked => false)+. This method still works, but it's encouraged to use +first_or_create+ because it's more explicit on what arguments are used to _find_ the record and what arguments are used to _create_ it, resulting in less confusion overall.
+
+h4. +first_or_create!+
+
+You can also use +first_or_create!+ to raise an exception if the new record is invalid. Validations are not covered on this guide, but let's assume for a moment that you temporarily add
+
+<ruby>
+ validates :orders_count, :presence => true
+</ruby>
+
+to your +Client+ model. If you try to create a new +Client+ without passing an +orders_count+, the record will be invalid and an exception will be raised:
+
+<ruby>
+Client.where(:first_name => 'Andy').first_or_create!(:locked => false)
+# => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank
+</ruby>
+
+NOTE: Be sure to check the extensive *Active Record Validations and Callbacks Guide* for more information about validations.
+
+h4. +first_or_new+
+
+The +first_or_new+ method will work just like +first_or_create+ but it will not call +create+ but +new+. This means that a new model instance will be created in memory but won't be saved to the database. Continuing with the +first_or_create+ example, we now want the client named 'Nick':
+
+<ruby>
+nick = Client.where(:first_name => 'Nick').first_or_new(:locked => false)
+# => <Client id: nil, first_name: "Nick", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
+
+nick.persisted?
+# => false
+
+nick.new_record?
+# => true
+</ruby>
+
+Because the object is not yet stored in the database, the SQL generated looks like this:
+
+<sql>
+SELECT * FROM clients WHERE (clients.first_name = 'Nick') LIMIT 1
+</sql>
+
+When you want to save it to the database, just call +save+:
<ruby>
-client = Client.find_or_initialize_by_first_name('Ryan')
+nick.save
+# => true
</ruby>
-will either assign an existing client object with the name "Ryan" to the client local variable, or initialize a new object similar to calling +Client.new(:first_name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
+Just like you can use *+build+* instead of *+new+*, you can use *+first_or_build+* instead of *+first_or_new+*.
h3. Finding by SQL
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index b2436a2e68..9e2fe2c694 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -1020,7 +1020,7 @@ class A
class_attribute :x, :instance_reader => false
end
-A.x = 1 # NoMethodError
+A.new.x = 1 # NoMethodError
</ruby>
For convenience +class_attribute+ also defines an instance predicate which is the double negation of what the instance reader returns. In the examples above it would be called +x?+.
diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile
index 554246acb3..211b02b94b 100644
--- a/railties/guides/source/asset_pipeline.textile
+++ b/railties/guides/source/asset_pipeline.textile
@@ -1,6 +1,6 @@
h2. Asset Pipeline
-This guide covers the ideology of the asset pipeline introduced in Rails 3.1.
+This guide covers the asset pipeline introduced in Rails 3.1.
By referring to this guide you will be able to:
* Understand what the asset pipeline is and what it does
@@ -13,13 +13,13 @@ endprologue.
h3. What is the Asset Pipeline?
-The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, SCSS and ERB.
+The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, Sass and ERB.
-Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets. Rails 3.1 includes the +sprockets-rails+ gem, which depends on the +sprockets+ gem, by default.
+Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets. Rails 3.1 is integrated with Sprockets through Action Pack which depends on the +sprockets+ gem, by default.
By having this as a core feature of Rails, all developers can benefit from the power of having their assets pre-processed, compressed and minified by one central library, Sprockets. This is part of Rails' "Fast by default" strategy as outlined by DHH in his 2011 keynote at Railsconf.
-In new Rails 3.1 application the asset pipeline is enabled by default. It can be disabled in +application.rb+ by putting this line inside the +Application+ class definition:
+In Rails 3.1, the asset pipeline is enabled by default. It can be disabled in +application.rb+ by putting this line inside the +Application+ class definition:
<plain>
config.assets.enabled = false
@@ -27,6 +27,7 @@ config.assets.enabled = false
It is recommended that you use the defaults for all new apps.
+
h4. Main Features
The first feature of the pipeline is to concatenate assets. This is important in a production environment, as it reduces the number of requests that a browser must make to render a web page. While Rails already has a feature to concatenate these types of assets -- by placing +:cache => true+ at the end of tags such as +javascript_include_tag+ and +stylesheet_link_tag+ -- many people do not use it.
@@ -35,7 +36,7 @@ The default behavior in Rails 3.1 and onward is to concatenate all files into on
The second feature is to minify or compress assets. For CSS, this usually involves removing whitespace and comments. For JavaScript, more complex processes can be applied. You can choose from a set of built in options or specify your own.
-The third feature is the ability to code these assets using another language, or language extension. These include SCSS or Sass for CSS, CoffeeScript for JavaScript, and ERB for both.
+The third feature is the ability to code these assets using another language, or language extension. These include Sass for CSS, CoffeeScript for JavaScript, and ERB for both.
h4. What is Fingerprinting and Why Should I Care?
@@ -74,21 +75,28 @@ The other problem is that when static assets are deployed with each new release
Fingerprinting avoids all these problems by ensuring filenames are consistent based on their content.
+Fingerprinting is enabled by default for production and disabled for all the others environments. You can enable or disable it in your configuration through the +config.assets.digest+ option.
+
More reading:
* "Optimize caching":http://code.google.com/speed/page-speed/docs/caching.html
* "Revving Filenames: don’t use querystring":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
+
h3. How to Use the Asset Pipeline
In previous versions of Rails, all assets were located in subdirectories of +public+ such as +images+, +javascripts+ and +stylesheets+. With the asset pipeline, the preferred location for these assets is now the +app/assets+ directory. Files in this directory are served by the Sprockets middleware included in the sprockets gem.
This is not to say that assets can (or should) no longer be placed in +public+; they still can be and will be served as static files by the application or web server. You would only use +app/assets+ if you wish your files to undergo some pre-processing before they are served.
-When a scaffold or controller is generated for the application, Rails also generates a JavaScript file (or CoffeeScript file if the +coffee-script+ gem is in the +Gemfile+) and a Cascading Style Sheet file (or SCSS file if +sass-rails+ is in the +Gemfile+) for that controller.
+In production, the default is to precompile these files to +public/assets+ so that they can be more efficiently delivered by the webserver.
+
+When a scaffold or controller is generated for the application, Rails also generates a JavaScript file (or CoffeeScript file if the +coffee-rails+ gem is in the +Gemfile+) and a Cascading Style Sheet file (or SCSS file if +sass-rails+ is in the +Gemfile+) for that controller.
For example, if a +ProjectsController+ is generated, there will be a new file at +app/assets/javascripts/projects.js.coffee+ and another at +app/assets/stylesheets/projects.css.scss+. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as +<%= javascript_include_tag params[:controller] %>+ or +<%= stylesheet_link_tag params[:controller] %>+.
+NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes.
+
h4. Asset Organization
Assets can be placed inside an application in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+.
@@ -99,13 +107,17 @@ Assets can be placed inside an application in one of three locations: +app/asset
+vendor/assets+ is for assets that are owned by outside entities, such as code for JavaScript plugins.
-All subdirectories that exist within these three locations are added to the search path for Sprockets (visible by calling +Rails.application.config.assets.paths+ in a console). When an asset is requested, these paths are looked through to see if they contain an asset matching the name specified. Once an asset has been found, it's processed by Sprockets and served.
+All subdirectories that exist within these three locations are added to the search path for Sprockets (visible by calling +Rails.application.config.assets.paths+ in a console). When an asset is requested, these paths are traversed to see if they contain an asset matching the name specified. Once an asset has been found, it's processed by Sprockets and served.
-h4. Coding Links to Assets
+You can add additional (fully qualified) paths to the pipeline in +application.rb+. For example:
-To access assets, you use the same tags that you are generally familiar with:
+<erb>
+config.assets.paths << File.join(Rails.root, 'app', 'assets', 'flash')
+</erb>
-Sprockets does not add any new methods to require your assets, you still use the familiar +javascript_include_tag+ and +stylesheet_link_tag+.
+h4. Coding Links to Assets
+
+Sprockets does not add any new methods to access your assets - you still use the familiar +javascript_include_tag+ and +stylesheet_link_tag+.
<erb>
<%= stylesheet_link_tag "application" %>
@@ -118,17 +130,27 @@ In regular views you can access images in the +assets/images+ directory like thi
<%= image_tag "rails.png" %>
</erb>
-Images can be organized into directories if required, and they can be accessed by specifying the directory's name in the tag:
+Provided that the pipeline is enabled within your application (and not disabled in the current environment context), this file is served by Sprockets. If a file exists at +public/assets/rails.png+ it is served by the webserver.
+
+Alternatively, a request for a file with an MD5 hash such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ is treated the same way. How these hashes are generated is covered in the "Production Assets":#production_assets section later on in this guide.
+
+Sprockets will also look through the paths specified in +config.assets.paths+ which includes the standard application paths and any path added by Rails engines.
+
+Images can also be organized into subdirectories if required, and they can be accessed by specifying the directory's name in the tag:
<erb>
<%= image_tag "icons/rails.png" %>
</erb>
-Providing that assets are enabled within your application (+config.assets.enabled+ in the current environment's file is not set to +false+), this file is served by Sprockets unless a file at +public/assets/rails.png+ exists, in which case that file is served.
+h5. CSS and ERB
+
+If you add an +erb+ extension to a CSS asset, making it something such as +application.css.erb+, then you can use the +asset_path+ helper in your CSS rules:
-Alternatively, a file with an MD5 hash after its name such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ is also picked up by Sprockets. How these hashes are generated is covered in the "Production Assets":#production_assets section later on in this guide.
+<plain>
+.class { background-image: url(<%= asset_path 'image.png' %>) }
+</plain>
-Otherwise, Sprockets looks through the available paths until it finds a file that matches the name and then serves it, first looking in the application's assets directories and then falling back to the various engines of the application.
+This writes the path to the particular asset being referenced. In this example, it would make sense to have an image in one of the asset load paths, such as +app/assets/images/image.png+, which would be referenced here. If this image is already available in +public/assets+ as a fingerprinted file, then that path is referenced.
If you want to use a "css data URI":http://en.wikipedia.org/wiki/Data_URI_scheme -- a method of embedding the image data directly into the CSS file -- you can use the +asset_data_uri+ helper.
@@ -138,29 +160,37 @@ If you want to use a "css data URI":http://en.wikipedia.org/wiki/Data_URI_scheme
This inserts a correctly-formatted data URI into the CSS source.
-h5. CSS and ERB
+Note that the closing tag cannot be of the style +-%>+.
-If you add an +erb+ extension to a CSS asset, making it something such as +application.css.erb+, then you can use the +asset_path+ helper in your CSS rules:
+h5. CSS and Sass
-<plain>
-.class { background-image: <%= asset_path 'image.png' %> }
-</plain>
+When using the asset pipeline, paths to assets must be re-written and +sass-rails+ provides +_url+ and +_path+ helpers for the following asset classes: image, font, video, audio, JavaScript and stylesheet.
-This writes the path to the particular asset being referenced. In this example, it would make sense to have an image in one of the asset load paths, such as +app/assets/images/image.png+, which would be referenced here. If this image is already available in +public/assets+ as a fingerprinted file, then that path is referenced.
+* +image_url("rails.png")+ becomes +url(/assets/rails.png)+.
+* +image_path("rails.png")+ becomes +"/assets/rails.png"+.
-Note that the closing tag cannot be of the style +-%>+.
+The more generic form can also be used but the asset path and class must both be specified:
-h5. CSS and SCSS
+* +asset_url("rails.png", image)+ becomes +url(/assets/rails.png)+.
+* +asset_path("rails.png", image)+ becomes +"/assets/rails.png"+.
-When using the asset pipeline, paths to assets must be re-written and +sass-rails+ provides +_url+ and +_path+ helpers for the following asset classes: image, font, video, audio, javascript, stylesheet.
+h5. JavaScript/CoffeeScript and ERB
-* +image_url("rails.png")+ becomes +url(/assets/rails.png)+
-* +image_path("rails.png")+ becomes +"/assets/rails.png"+.
+If you add an +erb+ extension to a JavaScript asset, making it something such as +application.js.erb+, then you can use the +asset_path+ helper in your JavaScript code:
-The more generic form can also be used but the asset path and class must both be specified:
+<plain>
+$('#logo').attr({
+ src: "<%= asset_path('logo.png') %>"
+});
+</plain>
-* +asset_url("rails.png", "image")+ becomes +url(/assets/rails.png)+
-* +asset_path("rails.png", "image")+ becomes +"/assets/rails.png"+
+This writes the path to the particular asset being referenced.
+
+Similarly, you can use the +asset_path+ helper in CoffeeScript files with +erb+ extension (eg. application.js.coffee.erb):
+
+<plain>
+$('#logo').attr src: "<% asset_path('logo.png') %>"
+</plain>
h4. Manifest Files and Directives
@@ -218,66 +248,89 @@ Keep in mind that the order of these pre-processors is important. For example, i
h3. In Development
-In the development environment assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-validate+ Cache-Control HTTP header to reduce request overhead on subsequent requests - on these the browser gets a 304 (not-modified) response.
-
-If any of the files in the manifest have changed between requests, the server responds with a new compiled file.
-
-h4. Debugging Assets
+In development mode assets are served as separate files in the order they are specified in the manifest file.
-You can put +?debug_assets=true+ or +?debug_assets=1+ at the end of a URL or set +config.assets.debug+ and Sprockets expands the lines which load the assets. For example, if you had an +app/assets/javascripts/application.js+ file containing these lines:
+This manifest +application.js+:
<plain>
-//= require "projects"
-//= require "tickets"
+//= require core
+//= require projects
+//= require tickets
</plain>
-By default, this only renders this line when used with +<%= javascript_include_tag "application" %>+ in a view or layout:
+would generate this HTML:
<html>
-<script src='/assets/application.js'></script>
+<script src='/assets/core.js?body=1'></script>
+<script src='/assets/projects.js?body=1'></script>
+<script src='/assets/tickets.js?body=1'></script>
</html>
-When the +debug_assets+ parameter is set, this line is expanded out into three separate lines, separating out the combined file into their parts.
+The +body+ param is required by Sprockets.
+
+h4. Turning Debugging off
+
+You can turn off debug mode by updating +development.rb+ to include:
+
+<erb>
+config.assets.debug = false
+</erb>
+
+When debug mode is off Sprockets will concatenate and run the necessary preprocessors on all files, generating the following HTML:
<html>
<script src='/assets/application.js'></script>
-<script src='/assets/projects.js'></script>
-<script src='/assets/tickets.js'></script>
</html>
-This allows the individual parts of an asset to be rendered and debugged separately.
+Assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-revalidate+ Cache-Control HTTP header to reduce request overhead on subsequent requests -- on these the browser gets a 304 (not-modified) response.
+
+If any of the files in the manifest have changed between requests, the server responds with a new compiled file.
+
+You can put +?debug_assets=true+ or +?debug_assets=1+ at the end of a URL to enable debug mode on-demand, and this will render individual tags for each file. This is useful for tracking down exact line numbers when debugging.
-Additionally if the +config.assets.debug+ is set to true you can debug your assets passing the +:debug+ option to the assets tags:
+Debug can also be set in the Rails helper methods:
<erb>
-<%= javascript_include_tag :application, :debug => true %>
+<%= stylesheet_link_tag "application", :debug => true %>
+<%= javascript_include_tag "application", :debug => true %>
</erb>
+The +:debug+ option is ignored if the debug mode is off.
+
+You could potentially also enable compression in development mode as a sanity check, and disable it on-demand as required for debugging.
-NOTE. Assets debugging is turned on by default in development and test environments. You can set +config.assets.allow_debugging+ to false to turn it off.
h3. In Production
-In the production environment, assets are served slightly differently.
+In the production environment Rails uses the fingerprinting scheme outlined above. By default it is assumed that assets have been precompiled and will be served as static assets by your web server.
-On the first request the assets are compiled and cached as described above, however the manifest names are altered to include an MD5 hash. Files names typically look like these:
+During the precompilation phase an MD5 is generated from the contents of the compiled files, and inserted into the filenames as they are written to disc. These fingerprinted names are used by the Rails helpers in place of the manifest name.
-<plain>
-/assets/application-908e25f4bf641868d8683022a5b62f54.js
-/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css
-</plain>
+For example this:
+
+<erb>
+<%= javascript_include_tag "application" %>
+<%= stylesheet_link_tag "application" %>
+</erb>
-The MD5 is generated from the contents of the compiled files, and is included in the HTTP +Content-MD5+ header.
+generates something like this:
-Sprockets also sets the +Cache-Control+ HTTP header to +max-age=31536000+. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache.
+<html>
+<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js" type="text/javascript"></script>
+<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" rel="stylesheet" type="text/css" />
+</html>
+
+The fingerprinting behavior is controlled by the setting of +config.assets.digest+ setting in Rails (which is +true+ for production, +false+ for everything else).
-This behavior is controlled by the setting of +config.action_controller.perform_caching+ setting in Rails (which is +true+ for production, +false+ for everything else). This value is propagated to Sprockets during initialization for use when action_controller is not available.
+NOTE: Under normal circumstances the default option should not be changed. If there are no digests in the filenames, and far-future headers are set, remote clients will never know to refetch the files when their content changes.
h4. Precompiling Assets
-Even though assets are served by Rack::Cache with far-future headers, in high traffic sites this may not be fast enough.
+Rails comes bundled with a rake task to compile the asset manifests and other files in the pipeline to the disk.
-Rails comes bundled with a rake task to compile the manifests to files on disc. These are located in the +public/assets+ directory where they are served by your web server instead of the Rails application.
+Compiled assets are written to the location specified in +config.assets.prefix+. The default setting will use the +public/assets+ directory.
+
+You must use this task either during deployment or locally if you do not have write access to your production filesystem.
The rake task is:
@@ -285,28 +338,53 @@ The rake task is:
bundle exec rake assets:precompile
</plain>
-Capistrano (v2.8.0+) has a recipe to handle this in deployment. Add the following line to +Capfile+:
+Capistrano (v2.8.0 and above) has a recipe to handle this in deployment. Add the following line to +Capfile+:
<erb>
load 'deploy/assets'
</erb>
-This links the folder specified in +config.assets.prefix+ to +shared/assets+. If you already use this folder you'll need to write your own deployment task.
+This links the folder specified in +config.assets.prefix+ to +shared/assets+. If you already use this shared folder you'll need to write your own deployment task.
It is important that this folder is shared between deployments so that remotely cached pages that reference the old compiled assets still work for the life of the cached page.
+NOTE. If you are precompiling your assets locally, you can use +bundle install --without assets+ on the server to avoid installing the assets gems (the gems in the assets group in the Gemfile).
+
The default matcher for compiling files includes +application.js+, +application.css+ and all files that do not end in +js+ or +css+:
<ruby>
[ /\w+\.(?!js|css).+/, /application.(css|js)$/ ]
</ruby>
-If you have other manifests or individual stylesheets and JavaScript files to include, you can append them to the +precompile+ array:
+If you have other manifests or individual stylesheets and JavaScript files to include, you can add them to the +precompile+ array:
<erb>
config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']
</erb>
+The rake task also generates a +manifest.yml+ that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods and avoids handing the mapping requests back to Sprockets. A typical manifest file looks like:
+
+<plain>
+---
+rails.png: rails-bd9ad5a560b5a3a7be0808c5cd76a798.png
+jquery-ui.min.js: jquery-ui-7e33882a28fc84ad0e0e47e46cbf901c.min.js
+jquery.min.js: jquery-8a50feed8d29566738ad005e19fe1c2d.min.js
+application.js: application-3fdab497b8fb70d20cfc5495239dfc29.js
+application.css: application-8af74128f904600e41a6e39241464e03.css
+</plain>
+
+The default location for the manifest is the root of the location specified in +config.assets.prefix+ ('/assets' by default).
+
+This can be changed with the +config.assets.manifest+ option. A fully specified path is required:
+
+<erb>
+config.assets.manifest = '/path/to/some/other/location'
+</erb>
+
+NOTE: If there are missing precompiled files in production you will get an <tt>AssetNoPrecompiledError</tt> exception indicating the name of the missing file(s).
+
+h5. Server Configuration
+
Precompiled assets exist on the filesystem and are served directly by your webserver. They do not have far-future headers by default, so to get the benefit of fingerprinting you'll have to update your server configuration to add them.
For Apache:
@@ -325,18 +403,69 @@ For Apache:
</LocationMatch>
</plain>
-TODO: NGINX instructions
+TODO: nginx instructions
-When files are precompiled, Sprockets also creates a "Gzip":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. This avoids the server having to do this for any requests; it can simply read the compressed files from disc. You must configure your server to use gzip compression and serve the compressed assets that will be stored in the public/assets folder. The following configuration options can be used:
+When files are precompiled, Sprockets also creates a "Gzip":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. This avoids the server having to do this for any requests; it can simply read the compressed files from disk. You must configure your server to use gzip compression and serve the compressed assets that will be stored in the +public/assets+ folder. The following configuration options can be used:
-TODO: Apache instructions
+For Apache:
-h3. Customizing the Pipeline
+<plain>
+<LocationMatch "^/assets/.*$">
+ # 2 lines to serve pre-gzipped version
+ RewriteCond %{REQUEST_FILENAME}.gz -s
+ RewriteRule ^(.+) $1.gz [L]
+</LocationMatch>
+
+# without these, Content-Type will be "application/x-gzip"
+<FilesMatch "^/assets/.*\.css.gz$">
+ ForceType text/css
+</FilesMatch>
+
+<FilesMatch "^/assets/.*\.js.gz$">
+ ForceType text/javascript
+</FilesMatch>
+</plain>
+
+For nginx:
+
+<plain>
+location ~ ^/(assets)/ {
+ root /path/to/public;
+ gzip_static on; # to serve pre-gzipped version
+ expires max;
+ add_header Cache-Control public;
+}
+</plain>
+
+h4. Live Compilation
+
+In some circumstances you may wish to use live compilation. In this mode all requests for assets in the pipeline are handled by Sprockets directly.
+
+To enable this option set:
+
+<erb>
+config.assets.compile = true
+</erb>
+
+On the first request the assets are compiled and cached as outlined in development above, and the manifest names used in the helpers are altered to include the MD5 hash.
+
+Sprockets also sets the +Cache-Control+ HTTP header to +max-age=31536000+. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache.
+
+This mode uses more memory, performs poorer than the default and is not recommended.
+
+When deploying a production application to a system without any pre-existing JavaScript runtimes, you may want to add one to your Gemfile:
+
+<plain>
+group :production do
+ gem 'therubyracer'
+end
+</plain>
+h3. Customizing the Pipeline
h4. CSS Compression
-There is currently one option for compressing CSS - YUI. This Gem extends the CSS syntax and offers minification.
+There is currently one option for compressing CSS, YUI. The "YUI CSS compressor":http://developer.yahoo.com/yui/compressor/css.html provides minification.
The following line enables YUI compression, and requires the +yui-compressor+ gem.
@@ -344,9 +473,9 @@ The following line enables YUI compression, and requires the +yui-compressor+ ge
config.assets.css_compressor = :yui
</erb>
-The +config.assets.compress+ must be set to +true+ to enable CSS compression
+The +config.assets.compress+ must be set to +true+ to enable CSS compression.
-h4. JavaScript
+h4. JavaScript Compression
Possible options for JavaScript compression are +:closure+, +:uglifier+ and +:yui+. These require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems respectively.
@@ -360,6 +489,8 @@ config.assets.js_compressor = :uglifier
The +config.assets.compress+ must be set to +true+ to enable JavaScript compression
+NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use +uglifier+. If you are using Mac OS X or Windows you have installed a JavaScript runtime in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes.
+
h4. Using Your Own Compressor
The compressor config settings for CSS and JavaScript also take any Object. This object must have a +compress+ method that takes a string as the sole argument and it must return a string.
@@ -378,6 +509,7 @@ To enable this, pass a +new+ Object to the config option in +application.rb+:
config.assets.css_compressor = Transformer.new
</erb>
+
h4. Changing the _assets_ Path
The public path that Sprockets uses by default is +/assets+.
@@ -405,7 +537,7 @@ WARNING: If you are upgrading an existing application and intend to use this opt
h3. How Caching Works
-Sprockets uses the default rails cache store to cache assets in dev and production. The only difference is file names are fingerprinted and get far-future headers in production.
+Sprockets uses the default rails cache store to cache assets in development and production.
TODO: Add more about changing the default store.
@@ -418,3 +550,69 @@ A good example of this is the +jquery-rails+ gem which comes with Rails as the s
h3. Making Your Library or Gem a Pre-Processor
TODO: Registering gems on "Tilt":https://github.com/rtomayko/tilt enabling Sprockets to find them.
+
+h3. Upgrading from Old Versions of Rails
+
+There are two issues when upgrading. The first is moving the files to the new locations. See the section above for guidance on the correct locations for different file types.
+
+The second is updating the various environment files with the correct default options. The following changes reflect the defaults in version 3.1.0.
+
+In +application.rb+:
+
+<erb>
+# Enable the asset pipeline
+config.assets.enabled = true
+
+# Version of your assets, change this if you want to expire all your assets
+config.assets.version = '1.0'
+
+# Change the path that assets are served from
+# config.assets.prefix = "/assets"
+</erb>
+
+In +development.rb+:
+
+<erb>
+# Do not compress assets
+config.assets.compress = false
+
+# Expands the lines which load the assets
+config.assets.debug = true
+</erb>
+
+And in +production.rb+:
+
+<erb>
+# Compress JavaScripts and CSS
+config.assets.compress = true
+
+# Choose the compressors to use
+# config.assets.js_compressor = :uglifier
+# config.assets.css_compressor = :yui
+
+# Don't fallback to assets pipeline if a precompiled asset is missed
+config.assets.compile = false
+
+# Generate digests for assets URLs.
+config.assets.digest = true
+
+# Defaults to Rails.root.join("public/assets")
+# config.assets.manifest = YOUR_PATH
+
+# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+# config.assets.precompile += %w( search.js )
+</erb>
+
+There are no changes to +test.rb+. The defaults in the test environment are: +config.assets.compile+ is true and +config.assets.compress+, +config.assets.debug+ and +config.assets.digest+ are false.
+
+The following should also be added to +Gemfile+:
+
+<plain>
+# Gems used only for assets and not required
+# in production environments by default.
+group :assets do
+ gem 'sass-rails', "~> 3.1.0"
+ gem 'coffee-rails', "~> 3.1.0"
+ gem 'uglifier'
+end
+</plain>
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 110c04f66e..ae84bb5b92 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -124,7 +124,7 @@ Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets wit
* +config.assets.compress+ a flag that enables the compression of compiled assets. It is explicitly set to true in +config/production.rb+.
-* +config.assets.css_compressor+ defines the CSS compressor to use. Only supported value at the moment is +:yui+, which uses the +yui-compressor+ gem.
+* +config.assets.css_compressor+ defines the CSS compressor to use. It is set by default by +sass-rails+. The unique alternative value at the moment is +:yui+, which uses the +yui-compressor+ gem.
* +config.assets.js_compressor+ defines the JavaScript compressor to use. Possible values are +:closure+, +:uglifier+ and +:yui+ which require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems respectively.
@@ -134,6 +134,18 @@ Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets wit
* +config.assets.prefix+ defines the prefix where assets are served from. Defaults to +/assets+.
+* +config.assets.digest+ enables the use of MD5 fingerprints in asset names. Set to +true+ by default in +production.rb+.
+
+* +config.assets.debug+ disables the concatenation and compression of assets. Set to +false+ by default in +development.rb+.
+
+* +config.assets.manifest+ defines the full path to be used for the asset precompiler's manifest file. Defaults to using +config.assets.prefix+.
+
+* +config.assets.cache_store+ defines the cache store that Sprockets will use. The default is the Rails file store.
+
+* +config.assets.version+ is an option string that is used in MD5 hash generation. This can be changed to force all files to be recompiled.
+
+* +config.assets.compile+ is a boolean that can be used to turn on live Sprockets compilation in production.
+
h4. Configuring Generators
diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile
index 4706725bb6..c302d393aa 100644
--- a/railties/guides/source/contributing_to_ruby_on_rails.textile
+++ b/railties/guides/source/contributing_to_ruby_on_rails.textile
@@ -284,7 +284,7 @@ $ cd rails
$ 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.
+It doesn’t really matter what name you use, because this branch will only exist on your local computer and your personal repository on Github. It won't be part of the Rails git repository.
h4. Write Your Code
diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile
index 2fa1d6e21d..3f990ef54b 100644
--- a/railties/guides/source/generators.textile
+++ b/railties/guides/source/generators.textile
@@ -449,6 +449,15 @@ The above code will put the following line into +Gemfile+:
gem "devise", :git => "git://github.com/plataformatec/devise", :branch => "master"
</ruby>
+h4. +gem_group+
+
+Wraps gem entries inside a group:
+
+<ruby>
+gem_group :development, :test do
+ gem "rspec-rails"
+end
+</ruby>
h4. +add_source+
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index d2bfcfdbb4..e1bc56823a 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -41,6 +41,9 @@ internet for learning Ruby, including:
* "Programming Ruby":http://www.ruby-doc.org/docs/ProgrammingRuby/
* "Why's (Poignant) Guide to Ruby":http://mislav.uniqpath.com/poignant-guide/
+Also, the example code for this guide is available in the rails github:https://github.com/rails/rails repository
+in rails/railties/guides/code/getting_started.
+
h3. What is Rails?
Rails is a web application development framework written in the Ruby language.
@@ -61,9 +64,9 @@ 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
-d o it, rather than requiring you to specify every little thing through endless configuration files.
+do it, rather than requiring you to specify every little thing through endless configuration files.
* REST is the best pattern for web applications - organizing your application around resources and standard HTTP verbs
-i s the fastest way to go.
+is the fastest way to go.
h4. The MVC Architecture
@@ -549,9 +552,9 @@ folders, and edit <tt>config/routes.rb</tt>. Here's a quick overview of what it
|app/views/posts/new.html.erb |A view to create a new post|
|app/views/posts/_form.html.erb |A partial to control the overall look and feel of the form used in edit and new views|
|app/helpers/posts_helper.rb |Helper functions to be used from the post views|
-|app/assets/stylesheets/scaffold.css.scss |Cascading style sheet to make the scaffolded views look better|
-|app/assets/stylesheets/post.css.scss |Cascading style sheet for the posts controller|
-|app/assets/javascripts/post.js.coffee |CoffeeScript for the posts controller|
+|app/assets/stylesheets/scaffolds.css.scss |Cascading style sheet to make the scaffolded views look better|
+|app/assets/stylesheets/posts.css.scss |Cascading style sheet for the posts controller|
+|app/assets/javascripts/posts.js.coffee |CoffeeScript for the posts controller|
|test/unit/post_test.rb |Unit testing harness for the posts model|
|test/functional/posts_controller_test.rb |Functional testing harness for the posts controller|
|test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper|
@@ -592,7 +595,7 @@ class CreatePosts < ActiveRecord::Migration
end
</ruby>
-The above migration creates a method name +change+ which will be called when you
+The above migration creates a method named +change+ which will be called when you
run this migration. The action defined in that method is also reversible, which
means Rails knows how to reverse the change made by this migration, in case you
want to reverse it at later date. By default, when you run this migration it
@@ -821,8 +824,8 @@ this layout in your editor and modify the +body+ tag:
<html>
<head>
<title>Blog</title>
- <%= stylesheet_link_tag :all %>
- <%= javascript_include_tag :defaults %>
+ <%= stylesheet_link_tag "application" %>
+ <%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body style="background: #EEEEEE;">
@@ -1604,7 +1607,7 @@ action, except for +index+ and +show+, so we write that:
<ruby>
class PostsController < ApplicationController
- http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
+ http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index, :show]
# GET /posts
# GET /posts.json
diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb
index 684251962c..214155c088 100644
--- a/railties/guides/source/index.html.erb
+++ b/railties/guides/source/index.html.erb
@@ -131,6 +131,10 @@ Ruby on Rails Guides
<%= guide("Caching with Rails", 'caching_with_rails.html', :work_in_progress => true) do %>
<p>Various caching techniques provided by Rails.</p>
<% end %>
+
+<%= guide('Asset Pipeline', 'asset_pipeline.html') do %>
+ <p>This guide documents the asset pipeline.</p>
+<% end %>
</dl>
<h3>Extending Rails</h3>
@@ -170,6 +174,10 @@ Ruby on Rails Guides
<h3>Release Notes</h3>
<dl>
+<%= guide("Ruby on Rails 3.1 Release Notes", '3_1_release_notes.html') do %>
+ <p>Release notes for Rails 3.1.</p>
+<% end %>
+
<%= guide("Ruby on Rails 3.0 Release Notes", '3_0_release_notes.html') do %>
<p>Release notes for Rails 3.0.</p>
<% end %>
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index 5dcac8e74c..4ef569013c 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -5,7 +5,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-<title><%= yield(:page_title) || 'Ruby on Rails guides' %></title>
+<title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
@@ -71,6 +71,7 @@
<dd><a href="configuring.html">Configuring Rails Applications</a></dd>
<dd><a href="command_line.html">Rails Command Line Tools and Rake Tasks</a></dd>
<dd><a href="caching_with_rails.html">Caching with Rails</a></dd>
+ <dd><a href="asset_pipeline.html">Asset Pipeline</a></dd>
<dt>Extending Rails</dt>
<dd><a href="plugins.html">The Basics of Creating Rails Plugins</a></dd>
@@ -83,6 +84,7 @@
<dd><a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails Guides Guidelines</a></dd>
<dt>Release Notes</dt>
+ <dd><a href="3_1_release_notes.html">Ruby on Rails 3.1 Release Notes</a></dd>
<dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 Release Notes</a></dd>
<dd><a href="2_3_release_notes.html">Ruby on Rails 2.3 Release Notes</a></dd>
<dd><a href="2_2_release_notes.html">Ruby on Rails 2.2 Release Notes</a></dd>
@@ -127,6 +129,10 @@
<%= link_to 'Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html' %>
for style and conventions.
</p>
+ <p>
+ If for whatever reason you spot something to fix but cannot patch it yourself, please
+ <%= link_to 'open an issue', 'https://github.com/rails/rails/issues' %>.
+ </p>
<p>And last but not least, any kind of discussion regarding Ruby on Rails
documentation is very welcome in the <%= link_to 'rubyonrails-docs mailing list', 'http://groups.google.com/group/rubyonrails-docs' %>.
</p>
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 87ba8ab82d..f49c2000ee 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -90,7 +90,7 @@ If we want to display the properties of all the books in our view, we can do so
<%= link_to 'New book', new_book_path %>
</ruby>
-NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), and +.builder+ for Builder (XML generator).
+NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. Beginning with Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), and +.builder+ for Builder (XML generator).
h4. Using +render+
@@ -515,7 +515,7 @@ h4. Using +redirect_to+
Another way to handle returning responses to an HTTP request is with +redirect_to+. As you've seen, +render+ tells Rails which view (or other asset) to use in constructing a response. The +redirect_to+ method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:
<ruby>
-redirect_to photos_path
+redirect_to photos_url
</ruby>
You can use +redirect_to+ with any arguments that you could use with +link_to+ or +url_for+. In addition, there's a special redirect that sends the user back to the page they just came from:
@@ -1093,6 +1093,13 @@ In Rails 3.0, there is also a shorthand for this. Assuming +@products+ is a coll
Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection:
+In the event that the collection is empty, +render+ will return nil, so it should be fairly simple to provide alternative content.
+
+<erb>
+<h1>Products</h1>
+<%= render(@products) || 'There are no products available.' %>
+</erb>
+
* +index.html.erb+
<erb>
diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile
index 75f81cf13d..5947735deb 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -207,7 +207,7 @@ GC Time measures the amount of time spent in GC for the performance test case.
h5. Metric Availability
-h6. Benchmarking
+h6(#benchmarking_1). Benchmarking
|_.Interpreter|_.Wall Time|_.Process Time|_.CPU Time|_.User Time|_.Memory|_.Objects|_.GC Runs|_.GC Time|
|_.MRI | yes | yes | yes | no | yes | yes | yes | yes |
@@ -215,7 +215,7 @@ h6. Benchmarking
|_.Rubinius | yes | no | no | no | yes | yes | yes | yes |
|_.JRuby | yes | no | no | yes | yes | yes | yes | yes |
-h6. Profiling
+h6(#profiling_1). Profiling
|_.Interpreter|_.Wall Time|_.Process Time|_.CPU Time|_.User Time|_.Memory|_.Objects|_.GC Runs|_.GC Time|
|_.MRI | yes | yes | no | no | yes | yes | yes | yes |
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index d3f9783fa6..e8bdfa7f1c 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -290,7 +290,7 @@ You can then return to the root directory (+cd ../..+) of your plugin and rerun
</shell>
-Getting closer...now we will implement the code of the acts_as_yaffle method to make the tests pass.
+Getting closer... Now we will implement the code of the acts_as_yaffle method to make the tests pass.
<ruby>
# yaffle/lib/yaffle/acts_as_yaffle.rb
@@ -322,7 +322,7 @@ When you run +rake+ you should see the tests all pass:
h4. Add an Instance Method
-This plugin will add a method named 'squawk' to any Active Record objects that call 'acts_as_yaffle'. The 'squawk'
+This plugin will add a method named 'squawk' to any Active Record object that calls 'acts_as_yaffle'. The 'squawk'
method will simply set the value of one of the fields in the database.
To start out, write a failing test that shows the behavior you'd like:
@@ -347,7 +347,7 @@ class ActsAsYaffleTest < Test::Unit::TestCase
assert_equal "squawk! Hello World", hickwall.last_squawk
end
- def test_wickwalls_squawk_should_populate_last_tweeted_at
+ def test_wickwalls_squawk_should_populate_last_tweet
wickwall = Wickwall.new
wickwall.squawk("Hello World")
assert_equal "squawk! Hello World", wickwall.last_tweet
@@ -355,7 +355,7 @@ class ActsAsYaffleTest < Test::Unit::TestCase
end
</ruby>
-Run the test to make sure the last two tests fail the an error that contains "NoMethodError: undefined method `squawk'",
+Run the test to make sure the last two tests fail with an error that contains "NoMethodError: undefined method `squawk'",
then update 'acts_as_yaffle.rb' to look like this:
<ruby>
@@ -400,11 +400,11 @@ the creation of generators can be found in the "Generators Guide":generators.htm
h3. Publishing your Gem
-Gem plugins in progress can be easily be shared from any Git repository. To share the Yaffle gem with others, simply
-commit the code to a Git repository (like Github) and add a line to the Gemfile of the any application:
+Gem plugins currently in development can easily be shared from any Git repository. To share the Yaffle gem with others, simply
+commit the code to a Git repository (like Github) and add a line to the Gemfile of the application in question:
<ruby>
-gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git'
+gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git'
</ruby>
After running +bundle install+, your gem functionality will be available to the application.
@@ -426,12 +426,12 @@ require 'yaffle'
</ruby>
You can test this by changing to the Rails application that you added the plugin to and starting a rails console. Once in the
-console we can check to see if the String has an instance method of to_squawk.
+console we can check to see if the String has an instance method to_squawk:
<shell>
$ cd my_app
$ rails console
-$ String.instance_methods.sort
+$ "Rails plugins are easy!".to_squawk
</shell>
You can also remove the .gemspec, Gemfile and Gemfile.lock files as they will no longer be needed.
@@ -445,9 +445,9 @@ The first step is to update the README file with detailed information about how
* Your name
* How to install
* How to add the functionality to the app (several examples of common use cases)
-* Warning, gotchas or tips that might help save users time
+* Warnings, gotchas or tips that might help users and save them time
-Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not part of the public api.
+Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public api.
Once your comments are good to go, navigate to your plugin directory and run:
diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile
index 566f8a0bdd..c3c8af4d3a 100644
--- a/railties/guides/source/rails_application_templates.textile
+++ b/railties/guides/source/rails_application_templates.textile
@@ -60,6 +60,18 @@ Please note that this will NOT install the gems for you and you will have to run
bundle install
</ruby>
+h4. gem_group(*names, &block)
+
+Wraps gem entries inside a group.
+
+For example, if you want to load +rspec-rails+ only in +development+ and +test+ group:
+
+<ruby>
+gem_group :development, :test do
+ gem "rspec-rails"
+end
+</ruby>
+
h4. add_source(source, options = {})
Adds the given source to the generated application's +Gemfile+.
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index cc55d1f756..caa0d91a83 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -931,7 +931,7 @@ class UserControllerTest < ActionController::TestCase
assert_equal "You have been invited by me@example.com", invite_email.subject
assert_equal 'friend@example.com', invite_email.to[0]
- assert_match /Hi friend@example.com/, invite_email.body
+ assert_match(/Hi friend@example.com/, invite_email.body)
end
end
</ruby>
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 5d7bd3282d..a48db3b6d2 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -37,15 +37,17 @@ module Rails
@assets = ActiveSupport::OrderedOptions.new
@assets.enabled = false
@assets.paths = []
- @assets.precompile = [ /\w+\.(?!js|css).+/, /application.(css|js)$/ ]
+ @assets.precompile = [ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) },
+ /application.(css|js)$/ ]
@assets.prefix = "/assets"
@assets.version = ''
@assets.debug = false
- @assets.allow_debugging = false
-
- @assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/" ]
- @assets.js_compressor = nil
- @assets.css_compressor = nil
+ @assets.compile = true
+ @assets.digest = false
+ @assets.manifest = nil
+ @assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/" ]
+ @assets.js_compressor = nil
+ @assets.css_compressor = nil
end
def compiled_asset_path
diff --git a/railties/lib/rails/application/route_inspector.rb b/railties/lib/rails/application/route_inspector.rb
new file mode 100644
index 0000000000..15c6d035e5
--- /dev/null
+++ b/railties/lib/rails/application/route_inspector.rb
@@ -0,0 +1,42 @@
+module Rails
+ class Application
+ ##
+ # This class is just used for displaying route information when someone
+ # executes `rake routes`. People should not use this class.
+ class RouteInspector # :nodoc:
+ def format all_routes, filter = nil
+ if filter
+ all_routes = all_routes.select{ |route| route.defaults[:controller] == filter }
+ end
+
+ routes = all_routes.collect do |route|
+
+ reqs = route.requirements.dup
+ rack_app = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
+
+ endpoint = rack_app ? rack_app.inspect : "#{reqs[:controller]}##{reqs[:action]}"
+ constraints = reqs.except(:controller, :action)
+
+ reqs = endpoint == '#' ? '' : endpoint
+
+ unless constraints.empty?
+ reqs = reqs.empty? ? constraints.inspect : "#{reqs} #{constraints.inspect}"
+ end
+
+ {:name => route.name.to_s, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
+ end
+
+ # Skip the route if it's internal info route
+ routes.reject! { |r| r[:path] =~ %r{/rails/info/properties|^/assets} }
+
+ name_width = routes.map{ |r| r[:name].length }.max
+ verb_width = routes.map{ |r| r[:verb].length }.max
+ path_width = routes.map{ |r| r[:path].length }.max
+
+ routes.map do |r|
+ "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index b8541c236e..575f4bb106 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -68,7 +68,32 @@ module Rails
end
in_root do
- append_file "Gemfile", "gem #{parts.join(", ")}\n", :verbose => false
+ str = "gem #{parts.join(", ")}\n"
+ str = " " + str if @in_group
+ append_file "Gemfile", str, :verbose => false
+ end
+ end
+
+ # Wraps gem entries inside a group.
+ #
+ # ==== Example
+ #
+ # gem_group :development, :test do
+ # gem "rspec-rails"
+ # end
+ #
+ def gem_group(*names, &block)
+ name = names.map(&:inspect).join(", ")
+ log :gemfile, "group #{name}"
+
+ in_root do
+ append_file "Gemfile", "\ngroup #{name} do\n", :force => true
+
+ @in_group = true
+ instance_eval &block
+ @in_group = false
+
+ append_file "Gemfile", "end\n", :force => true
end
end
@@ -97,7 +122,7 @@ module Rails
in_root do
if options[:env].nil?
- inject_into_file 'config/application.rb', "\n #{data}", :after => sentinel, :verbose => false
+ inject_into_file 'config/application.rb', "\n #{data}", :after => sentinel, :verbose => false
else
Array.wrap(options[:env]).each do |env|
inject_into_file "config/environments/#{env}.rb", "\n #{data}", :after => env_file_sentinel, :verbose => false
@@ -119,8 +144,8 @@ module Rails
if commands.is_a?(Symbol)
run "git #{commands}"
else
- commands.each do |command, options|
- run "git #{command} #{options}"
+ commands.each do |cmd, options|
+ run "git #{cmd} #{options}"
end
end
end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 21a2ae4e28..93f5023a7a 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -9,7 +9,7 @@ require 'uri'
module Rails
module Generators
class AppBase < Base
- DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db )
+ DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver )
JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
DATABASES.concat(JDBC_DATABASES)
@@ -154,12 +154,13 @@ module Rails
end
def gem_for_database
- # %w( mysql oracle postgresql sqlite3 frontbase ibm_db jdbcmysql jdbcsqlite3 jdbcpostgresql )
+ # %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql )
case options[:database]
when "oracle" then "ruby-oci8"
when "postgresql" then "pg"
when "frontbase" then "ruby-frontbase"
when "mysql" then "mysql2"
+ when "sqlserver" then "activerecord-sqlserver-adapter"
when "jdbcmysql" then "activerecord-jdbcmysql-adapter"
when "jdbcsqlite3" then "activerecord-jdbcsqlite3-adapter"
when "jdbcpostgresql" then "activerecord-jdbcpostgresql-adapter"
@@ -205,7 +206,7 @@ module Rails
group :assets do
gem 'sass-rails', :git => 'git://github.com/rails/sass-rails.git'
gem 'coffee-rails', :git => 'git://github.com/rails/coffee-rails.git'
- gem 'uglifier'
+ gem 'uglifier', '>= 1.0.3'
end
GEMFILE
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index c83e7ddf80..910cd16950 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -10,6 +10,9 @@ source 'http://rubygems.org'
<%= assets_gemfile_entry %>
<%= javascript_gemfile_entry %>
+# To use ActiveModel has_secure_password
+# gem 'bcrypt-ruby', '~> 3.0.0'
+
# Use unicorn as the web server
# gem 'unicorn'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 3891829150..13fbe9e526 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -52,6 +52,9 @@ module <%= app_const_base %>
<% unless options.skip_sprockets? -%>
# Enable the asset pipeline
config.assets.enabled = true
+
+ # Version of your assets, change this if you want to expire all your assets
+ config.assets.version = '1.0'
<% end -%>
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 33f9939ffe..47078e3af9 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -30,9 +30,6 @@
# Do not compress assets
config.assets.compress = false
- # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
- config.assets.allow_debugging = true
-
# Expands the lines which load the assets
config.assets.debug = true
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index de56d47688..64e2c09467 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -14,6 +14,15 @@
# Compress JavaScripts and CSS
config.assets.compress = true
+ # Don't fallback to assets pipeline if a precompiled asset is missed
+ config.assets.compile = false
+
+ # Generate digests for assets URLs
+ config.assets.digest = true
+
+ # Defaults to Rails.root.join("public/assets")
+ # config.assets.manifest = YOUR_PATH
+
# Specifies the header that your server uses for sending files
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index 8e33a65b2d..80198cc21e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -41,7 +41,4 @@
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
-
- # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
- config.assets.allow_debugging = true
end
diff --git a/railties/lib/rails/generators/rails/app/templates/public/500.html b/railties/lib/rails/generators/rails/app/templates/public/500.html
index b80307fc16..f3648a0dbc 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/500.html
+++ b/railties/lib/rails/generators/rails/app/templates/public/500.html
@@ -20,7 +20,6 @@
<!-- This file lives in public/500.html -->
<div class="dialog">
<h1>We're sorry, but something went wrong.</h1>
- <p>We've been notified about this issue and we'll take a look at it shortly.</p>
</div>
</body>
</html>
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index ee85b70bb5..7319fb79f6 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -81,7 +81,7 @@ module Rails
#
# assert_file "app/controller/products_controller.rb" do |controller|
# assert_instance_method :index, content do |index|
- # assert_match /Product\.all/, index
+ # assert_match(/Product\.all/, index)
# end
# end
#
@@ -148,7 +148,7 @@ module Rails
#
# assert_migration "db/migrate/create_products.rb" do |migration|
# assert_class_method :up, migration do |up|
- # assert_match /create_table/, up
+ # assert_match(/create_table/, up)
# end
# end
#
@@ -161,7 +161,7 @@ module Rails
#
# assert_file "app/controller/products_controller.rb" do |controller|
# assert_instance_method :index, content do |index|
- # assert_match /Product\.all/, index
+ # assert_match(/Product\.all/, index)
# end
# end
#
diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
index d4138ca2f5..5c8780aa64 100644
--- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
+++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
@@ -1,4 +1,4 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
<% unless attributes.empty? -%>
one:
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 0c26bcf790..7dc54144da 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -3,35 +3,7 @@ task :routes => :environment do
Rails.application.reload_routes!
all_routes = Rails.application.routes.routes
- if ENV['CONTROLLER']
- all_routes = all_routes.select{ |route| route.defaults[:controller] == ENV['CONTROLLER'] }
- end
-
- routes = all_routes.collect do |route|
-
- reqs = route.requirements.dup
- rack_app = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
-
- endpoint = rack_app ? rack_app.inspect : "#{reqs[:controller]}##{reqs[:action]}"
- constraints = reqs.except(:controller, :action)
-
- reqs = endpoint == '#' ? '' : endpoint
-
- unless constraints.empty?
- reqs = reqs.empty? ? constraints.inspect : "#{reqs} #{constraints.inspect}"
- end
-
- {:name => route.name.to_s, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
- end
-
- # Skip the route if it's internal info route
- routes.reject! { |r| r[:path] =~ %r{/rails/info/properties|^/assets} }
-
- name_width = routes.map{ |r| r[:name].length }.max
- verb_width = routes.map{ |r| r[:verb].length }.max
- path_width = routes.map{ |r| r[:path].length }.max
-
- routes.each do |r|
- puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
- end
+ require 'rails/application/route_inspector'
+ inspector = Rails::Application::RouteInspector.new
+ puts inspector.format(all_routes, ENV['CONTROLLER']).join "\n"
end
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index 38e1e21d17..1b99af22a4 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -33,24 +33,33 @@ module ApplicationTests
teardown_app
end
- test "assets are concatenated when debug is off and allow_debugging is off either if debug_assets param is provided" do
- # config.assets.debug and config.assets.allow_debugging are false for production environment
+ test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
+ # config.assets.debug and config.assets.compile are false for production environment
+ ENV["RAILS_ENV"] = "production"
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
require "#{app_path}/config/environment"
- # the debug_assets params isn't used if allow_debugging is off
+ class ::PostsController < ActionController::Base ; end
+
+ # the debug_assets params isn't used if compile is off
get '/posts?debug_assets=true'
- assert_match %r{<script src="/assets/application-([0-z]+)\.js" type="text/javascript"></script>}, last_response.body
- assert_no_match %r{<script src="/assets/xmlhr-([0-z]+)\.js" type="text/javascript"></script>}, last_response.body
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js" type="text\/javascript"><\/script>/, last_response.body)
+ assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js" type="text\/javascript"><\/script>/, last_response.body)
end
- test "assets aren't concatened when allow_debugging is on and debug_assets params is true" do
- app_file "config/initializers/allow_debugging.rb", "Rails.application.config.assets.allow_debugging = true"
+ test "assets aren't concatened when compile is true is on and debug_assets params is true" do
+ app_file "config/initializers/compile.rb", "Rails.application.config.assets.compile = true"
+ ENV["RAILS_ENV"] = "production"
require "#{app_path}/config/environment"
+ class ::PostsController < ActionController::Base ; end
+
get '/posts?debug_assets=true'
- assert_match %r{<script src="/assets/application-([0-z]+)\.js\?body=1" type="text/javascript"></script>}, last_response.body
- assert_match %r{<script src="/assets/xmlhr-([0-z]+)\.js\?body=1" type="text/javascript"></script>}, last_response.body
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js\?body=1" type="text\/javascript"><\/script>/, last_response.body)
+ assert_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js\?body=1" type="text\/javascript"><\/script>/, last_response.body)
end
end
end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index a8d1382e94..b8a8665b3e 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -37,6 +37,8 @@ module ApplicationTests
test "assets do not require compressors until it is used" do
app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
+ add_to_config "config.assets.compile = true"
+
ENV["RAILS_ENV"] = "production"
require "#{app_path}/config/environment"
@@ -62,25 +64,208 @@ module ApplicationTests
end
end
+ test "precompile application.js and application.css and all other files not ending with .js or .css by default" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ app_file "app/assets/stylesheets/application.css", "body{}"
+ app_file "app/assets/javascripts/something.min.js", "alert();"
+ app_file "app/assets/stylesheets/something.min.css", "body{}"
+
+ images_should_compile = ["a.png", "happyface.png", "happy_face.png", "happy.face.png",
+ "happy-face.png", "happy.happy_face.png", "happy_happy.face.png",
+ "happy.happy.face.png", "happy", "happy.face", "-happyface",
+ "-happy.png", "-happy.face.png", "_happyface", "_happy.face.png",
+ "_happy.png"]
+ images_should_compile.each do |filename|
+ app_file "app/assets/images/#{filename}", "happy"
+ end
+
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+
+ images_should_compile.each do |filename|
+ assert File.exists?("#{app_path}/public/assets/#{filename}")
+ end
+ assert File.exists?("#{app_path}/public/assets/application.js")
+ assert File.exists?("#{app_path}/public/assets/application.css")
+ assert !File.exists?("#{app_path}/public/assets/something.min.js")
+ assert !File.exists?("#{app_path}/public/assets/something.min.css")
+ end
+
+ test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
+ add_to_config "config.assets.digest = true"
+ add_to_config "config.action_controller.perform_caching = false"
+
+ ENV["RAILS_ENV"] = "production"
+ require "#{app_path}/config/environment"
+
+ assert_equal Sprockets::Index, Rails.application.assets.class
+ end
+
+ test "precompile creates a manifest file with all the assets listed" do
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ app_file "app/assets/javascripts/application.js", "alert();"
+ # digest is default in false, we must enable it for test environment
+ add_to_config "config.assets.digest = true"
+
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+
+ manifest = "#{app_path}/public/assets/manifest.yml"
+
+ assets = YAML.load_file(manifest)
+ assert_match(/application-([0-z]+)\.js/, assets["application.js"])
+ assert_match(/application-([0-z]+)\.css/, assets["application.css"])
+ end
+
+ test "precompile creates a manifest file in a custom path with all the assets listed" do
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ app_file "app/assets/javascripts/application.js", "alert();"
+ # digest is default in false, we must enable it for test environment
+ add_to_config "config.assets.digest = true"
+ add_to_config "config.assets.manifest = '#{app_path}/shared'"
+ FileUtils.mkdir "#{app_path}/shared"
+
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+
+ manifest = "#{app_path}/shared/manifest.yml"
+
+ assets = YAML.load_file(manifest)
+ assert_match(/application-([0-z]+)\.js/, assets["application.js"])
+ assert_match(/application-([0-z]+)\.css/, assets["application.css"])
+ end
+
+
+ test "the manifest file should be saved by default in the same assets folder" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ # digest is default in false, we must enable it for test environment
+ add_to_config "config.assets.digest = true"
+ add_to_config "config.assets.prefix = '/x'"
+
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+
+ manifest = "#{app_path}/public/x/manifest.yml"
+ assets = YAML.load_file(manifest)
+ assert_match(/application-([0-z]+)\.js/, assets["application.js"])
+ end
+
+ test "precompile does not append asset digests when config.assets.digest is false" do
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ app_file "app/assets/javascripts/application.js", "alert();"
+ add_to_config "config.assets.digest = false"
+
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+
+ assert File.exists?("#{app_path}/public/assets/application.js")
+ assert File.exists?("#{app_path}/public/assets/application.css")
+
+ manifest = "#{app_path}/public/assets/manifest.yml"
+
+ assets = YAML.load_file(manifest)
+ assert_equal "application.js", assets["application.js"]
+ assert_equal "application.css", assets["application.css"]
+ end
+
+ test "assets do not require any assets group gem when manifest file is present" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+
+ ENV["RAILS_ENV"] = "production"
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+ manifest = "#{app_path}/public/assets/manifest.yml"
+ assets = YAML.load_file(manifest)
+ asset_path = assets["application.js"]
+
+ require "#{app_path}/config/environment"
+
+ # Checking if Uglifier is defined we can know if Sprockets was reached or not
+ assert !defined?(Uglifier)
+ get "/assets/#{asset_path}"
+ assert_match "alert()", last_response.body
+ assert !defined?(Uglifier)
+ end
+
+ test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled" do
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>"
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match '/posts', :to => "posts#index"
+ end
+ RUBY
+
+ ENV["RAILS_ENV"] = "production"
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+
+ # Create file after of precompile
+ app_file "app/assets/javascripts/app.js", "alert();"
+
+ require "#{app_path}/config/environment"
+ class ::PostsController < ActionController::Base ; end
+
+ get '/posts'
+ assert_match(/AssetNotPrecompiledError/, last_response.body)
+ assert_match(/app.js isn't precompiled/, last_response.body)
+ end
+
+ test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled if digest is disabled" do
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>"
+ add_to_config "config.assets.compile = false"
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match '/posts', :to => "posts#index"
+ end
+ RUBY
+
+ ENV["RAILS_ENV"] = "development"
+ capture(:stdout) do
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
+ end
+
+ # Create file after of precompile
+ app_file "app/assets/javascripts/app.js", "alert();"
+
+ require "#{app_path}/config/environment"
+ class ::PostsController < ActionController::Base ; end
+
+ get '/posts'
+ assert_match(/AssetNotPrecompiledError/, last_response.body)
+ assert_match(/app.js isn't precompiled/, last_response.body)
+ end
+
test "precompile appends the md5 hash to files referenced with asset_path and run in the provided RAILS_ENV" do
app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ # digest is default in false, we must enable it for test environment
+ add_to_config "config.assets.digest = true"
# capture(:stdout) do
Dir.chdir(app_path){ `bundle exec rake assets:precompile RAILS_ENV=test` }
# end
file = Dir["#{app_path}/public/assets/application-*.css"].first
- assert_match /\/assets\/rails-([0-z]+)\.png/, File.read(file)
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
end
test "precompile appends the md5 hash to files referenced with asset_path and run in production as default even using RAILS_GROUPS=assets" do
app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ add_to_config "config.assets.compile = true"
ENV["RAILS_ENV"] = nil
capture(:stdout) do
Dir.chdir(app_path){ `bundle exec rake assets:precompile RAILS_GROUPS=assets` }
end
file = Dir["#{app_path}/public/assets/application-*.css"].first
- assert_match /\/assets\/rails-([0-z]+)\.png/, File.read(file)
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
end
test "assets are cleaned up properly" do
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index cc65a674c9..3183162e6c 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -79,13 +79,13 @@ module ApplicationTests
silence_stderr do
output = Dir.chdir(app_path){ `rake test` }
- assert_match /Errors running test:units! #<ActiveRecord::AdapterNotSpecified/, output
- assert_match /Errors running test:functionals! #<RuntimeError/, output
- assert_match /Errors running test:integration! #<RuntimeError/, output
+ assert_match(/Errors running test:units! #<ActiveRecord::AdapterNotSpecified/, output)
+ assert_match(/Errors running test:functionals! #<RuntimeError/, output)
+ assert_match(/Errors running test:integration! #<RuntimeError/, output)
end
end
- def test_rake_routes_output_strips_anchors_from_http_verbs
+ def test_rake_routes_calls_the_route_inspector
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
get '/cart', :to => 'cart#show'
@@ -94,99 +94,6 @@ module ApplicationTests
assert_equal "cart GET /cart(.:format) cart#show\n", Dir.chdir(app_path){ `rake routes` }
end
- def test_rake_routes_shows_custom_assets
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- get '/custom/assets', :to => 'custom_assets#show'
- end
- RUBY
- assert_equal "custom_assets GET /custom/assets(.:format) custom_assets#show\n",
- Dir.chdir(app_path){ `rake routes` }
- end
-
- def test_rake_routes_shows_resources_route
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- resources :articles
- end
- RUBY
- expected =
- " articles GET /articles(.:format) articles#index\n" <<
- " POST /articles(.:format) articles#create\n" <<
- " new_article GET /articles/new(.:format) articles#new\n" <<
- "edit_article GET /articles/:id/edit(.:format) articles#edit\n" <<
- " article GET /articles/:id(.:format) articles#show\n" <<
- " PUT /articles/:id(.:format) articles#update\n" <<
- " DELETE /articles/:id(.:format) articles#destroy\n"
- assert_equal expected, Dir.chdir(app_path){ `rake routes` }
- end
-
- def test_rake_routes_shows_root_route
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- root :to => 'pages#main'
- end
- RUBY
- assert_equal "root / pages#main\n", Dir.chdir(app_path){ `rake routes` }
- end
-
- def test_rake_routes_shows_controller_and_action_only_route
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- match ':controller/:action'
- end
- RUBY
- assert_equal " /:controller/:action(.:format) \n", Dir.chdir(app_path){ `rake routes` }
- end
-
- def test_rake_routes_shows_controller_and_action_route_with_constraints
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- match ':controller(/:action(/:id))', :id => /\\d+/
- end
- RUBY
- assert_equal " /:controller(/:action(/:id))(.:format) {:id=>/\\d+/}\n", Dir.chdir(app_path){ `rake routes` }
- end
-
- def test_rake_routes_shows_route_with_defaults
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- match 'photos/:id' => 'photos#show', :defaults => {:format => 'jpg'}
- end
- RUBY
- assert_equal %Q[ /photos/:id(.:format) photos#show {:format=>"jpg"}\n], Dir.chdir(app_path){ `rake routes` }
- end
-
- def test_rake_routes_shows_route_with_constraints
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- match 'photos/:id' => 'photos#show', :id => /[A-Z]\\d{5}/
- end
- RUBY
- assert_equal " /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}\n", Dir.chdir(app_path){ `rake routes` }
- end
-
- def test_rake_routes_shows_route_with_rack_app
- app_file "lib/rack_app.rb", <<-RUBY
- class RackApp
- class << self
- def call(env)
- end
- end
- end
- RUBY
-
- app_file "config/routes.rb", <<-RUBY
- require 'rack_app'
-
- AppTemplate::Application.routes.draw do
- match 'foo/:id' => RackApp, :id => /[A-Z]\\d{5}/
- end
- RUBY
-
- assert_equal " /foo/:id(.:format) RackApp {:id=>/[A-Z]\\d{5}/}\n", Dir.chdir(app_path){ `rake routes` }
- end
-
def test_logger_is_flushed_when_exiting_production_rake_tasks
add_to_config <<-RUBY
rake_tasks do
@@ -219,6 +126,55 @@ module ApplicationTests
assert_match(/AddEmailToUsers: reverted/, output)
end
+ def test_migration_status_when_schema_migrations_table_is_not_present
+ output = Dir.chdir(app_path){ `rake db:migrate:status` }
+ assert_equal "Schema migrations table does not exist yet.\n", output
+ end
+
+ def test_migration_status
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ `rails generate migration add_email_to_users email:string`
+ end
+
+ Dir.chdir(app_path) { `rake db:migrate`}
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:rollback STEP=1` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+ end
+
+ def test_migration_status_after_rollback_and_redo
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ `rails generate migration add_email_to_users email:string`
+ end
+
+ Dir.chdir(app_path) { `rake db:migrate`}
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:rollback STEP=2` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/down\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:migrate:redo` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+ end
+
def test_loading_specific_fixtures
Dir.chdir(app_path) do
`rails generate model user username:string password:string`
@@ -245,4 +201,4 @@ module ApplicationTests
assert_match(/7 tests, 10 assertions, 0 failures, 0 errors/, content)
end
end
-end \ No newline at end of file
+end
diff --git a/railties/test/application/route_inspect_test.rb b/railties/test/application/route_inspect_test.rb
new file mode 100644
index 0000000000..add8256b5d
--- /dev/null
+++ b/railties/test/application/route_inspect_test.rb
@@ -0,0 +1,97 @@
+require 'test/unit'
+require 'rails/application/route_inspector'
+require 'action_controller'
+
+module ApplicationTests
+ class RouteInspectTest < Test::Unit::TestCase
+ def setup
+ @set = ActionDispatch::Routing::RouteSet.new
+ @inspector = Rails::Application::RouteInspector.new
+ end
+
+ def test_cart_inspect
+ @set.draw do
+ get '/cart', :to => 'cart#show'
+ end
+ output = @inspector.format @set.routes
+ assert_equal ["cart GET /cart(.:format) cart#show"], output
+ end
+
+ def test_inspect_shows_custom_assets
+ @set.draw do
+ get '/custom/assets', :to => 'custom_assets#show'
+ end
+ output = @inspector.format @set.routes
+ assert_equal ["custom_assets GET /custom/assets(.:format) custom_assets#show"], output
+ end
+
+ def test_inspect_routes_shows_resources_route
+ @set.draw do
+ resources :articles
+ end
+ output = @inspector.format @set.routes
+ expected = [
+ " articles GET /articles(.:format) articles#index",
+ " POST /articles(.:format) articles#create",
+ " new_article GET /articles/new(.:format) articles#new",
+ "edit_article GET /articles/:id/edit(.:format) articles#edit",
+ " article GET /articles/:id(.:format) articles#show",
+ " PUT /articles/:id(.:format) articles#update",
+ " DELETE /articles/:id(.:format) articles#destroy" ]
+ assert_equal expected, output
+ end
+
+ def test_inspect_routes_shows_root_route
+ @set.draw do
+ root :to => 'pages#main'
+ end
+ output = @inspector.format @set.routes
+ assert_equal ["root / pages#main"], output
+ end
+
+ def test_inspect_routes_shows_controller_and_action_only_route
+ @set.draw do
+ match ':controller/:action'
+ end
+ output = @inspector.format @set.routes
+ assert_equal [" /:controller/:action(.:format) "], output
+ end
+
+ def test_inspect_routes_shows_controller_and_action_route_with_constraints
+ @set.draw do
+ match ':controller(/:action(/:id))', :id => /\d+/
+ end
+ output = @inspector.format @set.routes
+ assert_equal [" /:controller(/:action(/:id))(.:format) {:id=>/\\d+/}"], output
+ end
+
+ def test_rake_routes_shows_route_with_defaults
+ @set.draw do
+ match 'photos/:id' => 'photos#show', :defaults => {:format => 'jpg'}
+ end
+ output = @inspector.format @set.routes
+ assert_equal [%Q[ /photos/:id(.:format) photos#show {:format=>"jpg"}]], output
+ end
+
+ def test_rake_routes_shows_route_with_constraints
+ @set.draw do
+ match 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
+ end
+ output = @inspector.format @set.routes
+ assert_equal [" /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}"], output
+ end
+
+ class RackApp
+ def self.call(env)
+ end
+ end
+
+ def test_rake_routes_shows_route_with_rack_app
+ @set.draw do
+ match 'foo/:id' => RackApp, :id => /[A-Z]\d{5}/
+ end
+ output = @inspector.format @set.routes
+ assert_equal [" /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}"], output
+ end
+ end
+end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index 3adf0ccd3e..a05e39658d 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -141,7 +141,7 @@ module ApplicationTests
test "routes appending blocks" do
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- match ':controller#:action'
+ match ':controller/:action'
end
RUBY
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index e4a8000425..94e9abb3cc 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -102,11 +102,25 @@ class ActionsTest < Rails::Generators::TestCase
assert_file 'Gemfile', /gem "rspec-rails"$/
end
+ def test_gem_group_should_wrap_gems_in_a_group
+ run_generator
+
+ action :gem_group, :development, :test do
+ gem 'rspec-rails'
+ end
+
+ action :gem_group, :test do
+ gem 'fakeweb'
+ end
+
+ assert_file 'Gemfile', /\ngroup :development, :test do\n gem "rspec-rails"\nend\n\ngroup :test do\n gem "fakeweb"\nend/
+ end
+
def test_environment_should_include_data_in_environment_initializer_block
run_generator
autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
action :environment, autoload_paths
- assert_file 'config/application.rb', /#{Regexp.escape(autoload_paths)}/
+ assert_file 'config/application.rb', / class Application < Rails::Application\n #{Regexp.escape(autoload_paths)}/
end
def test_environment_should_include_data_in_environment_initializer_block_with_env_option
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index b49945f153..9183945619 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -197,8 +197,8 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_file "app/helpers/bukkits/application_helper.rb", /module Bukkits\n module ApplicationHelper/
assert_file "app/views/layouts/bukkits/application.html.erb" do |contents|
assert_match "<title>Bukkits</title>", contents
- assert_match /stylesheet_link_tag\s+['"]bukkits\/application['"]/, contents
- assert_match /javascript_include_tag\s+['"]bukkits\/application['"]/, contents
+ assert_match(/stylesheet_link_tag\s+['"]bukkits\/application['"]/, contents)
+ assert_match(/javascript_include_tag\s+['"]bukkits\/application['"]/, contents)
end
end
@@ -241,7 +241,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
run_generator [destination_root, "--skip-test-unit"]
assert_no_file "test"
assert_file "bukkits.gemspec" do |contents|
- assert_no_match /s.test_files = Dir\["test\/\*\*\/\*"\]/, contents
+ assert_no_match(/s.test_files = Dir\["test\/\*\*\/\*"\]/, contents)
end
end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index d3074afd91..1534f0d828 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -191,11 +191,11 @@ module SharedCustomGeneratorTests
end
def test_builder_option_with_http
- path = "http://gist.github.com/103208.txt"
+ url = "http://gist.github.com/103208.txt"
template = "class #{builder_class}; end"
template.instance_eval "def read; self; end" # Make the string respond to read
- generator([destination_root], :builder => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
+ generator([destination_root], :builder => url).expects(:open).with(url, 'Accept' => 'application/x-thor-template').returns(template)
quietly { generator.invoke_all }
default_files.each{ |path| assert_no_file(path) }
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 0ff1e0f180..06a60cd858 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -400,7 +400,7 @@ module RailtiesTest
boot_rails
get("/bukkits/posts/new")
- assert_match /name="post\[title\]"/, last_response.body
+ assert_match(/name="post\[title\]"/, last_response.body)
end
test "isolated engine should set correct route module prefix for nested namespace" do
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index 94dec405a7..0491fc2174 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -11,13 +11,17 @@ module ApplicationTests
add_to_config("config.action_dispatch.show_exceptions = false")
+ @simple_plugin = engine "weblog"
@plugin = engine "blog"
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
+ mount Weblog::Engine, :at => '/', :as => 'weblog'
resources :posts
match "/engine_route" => "application_generating#engine_route"
match "/engine_route_in_view" => "application_generating#engine_route_in_view"
+ match "/weblog_engine_route" => "application_generating#weblog_engine_route"
+ match "/weblog_engine_route_in_view" => "application_generating#weblog_engine_route_in_view"
match "/url_for_engine_route" => "application_generating#url_for_engine_route"
match "/polymorphic_route" => "application_generating#polymorphic_route"
match "/application_polymorphic_path" => "application_generating#application_polymorphic_path"
@@ -28,6 +32,29 @@ module ApplicationTests
end
RUBY
+
+ @simple_plugin.write "lib/weblog.rb", <<-RUBY
+ module Weblog
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ @simple_plugin.write "config/routes.rb", <<-RUBY
+ Weblog::Engine.routes.draw do
+ match '/weblog' => "weblogs#index", :as => 'weblogs'
+ end
+ RUBY
+
+ @simple_plugin.write "app/controllers/weblogs_controller.rb", <<-RUBY
+ class WeblogsController < ActionController::Base
+ def index
+ render :text => request.url
+ end
+ end
+ RUBY
+
+
@plugin.write "app/models/blog/post.rb", <<-RUBY
module Blog
class Post
@@ -100,6 +127,14 @@ module ApplicationTests
render :inline => "<%= blog.posts_path %>"
end
+ def weblog_engine_route
+ render :text => weblog.weblogs_path
+ end
+
+ def weblog_engine_route_in_view
+ render :inline => "<%= weblog.weblogs_path %>"
+ end
+
def url_for_engine_route
render :text => blog.url_for(:controller => "blog/posts", :action => "index", :user => "john", :only_path => true)
end
@@ -192,5 +227,13 @@ module ApplicationTests
get "/application_polymorphic_path"
assert_equal "/posts/44", last_response.body
end
+
+ test "route path for controller action when engine is mounted at root" do
+ get "/weblog_engine_route"
+ assert_equal "/weblog", last_response.body
+
+ get "/weblog_engine_route_in_view"
+ assert_equal "/weblog", last_response.body
+ end
end
end
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index 9a64b7c64e..21fde49ff7 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -61,21 +61,21 @@ module RailtiesTest
assert File.exists?("#{app_path}/db/migrate/2_create_users.rb")
assert File.exists?("#{app_path}/db/migrate/3_add_last_name_to_users.rb")
- assert_match /Copied migration 2_create_users.rb from bukkits/, output
- assert_match /Copied migration 3_add_last_name_to_users.rb from bukkits/, output
- assert_match /NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/, output
+ assert_match(/Copied migration 2_create_users.rb from bukkits/, output)
+ assert_match(/Copied migration 3_add_last_name_to_users.rb from bukkits/, output)
+ assert_match(/NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/, output)
assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length
output = `bundle exec rake railties:install:migrations`.split("\n")
assert File.exists?("#{app_path}/db/migrate/4_create_yaffles.rb")
- assert_no_match /2_create_users/, output.join("\n")
+ assert_no_match(/2_create_users/, output.join("\n"))
yaffle_migration_order = output.index(output.detect{|o| /Copied migration 4_create_yaffles.rb from acts_as_yaffle/ =~ o })
bukkits_migration_order = output.index(output.detect{|o| /NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/ =~ o })
assert_not_nil yaffle_migration_order, "Expected migration to be copied"
assert_not_nil bukkits_migration_order, "Expected migration to be skipped"
- assert_equal (railties.index('acts_as_yaffle') > railties.index('bukkits')) , (yaffle_migration_order > bukkits_migration_order)
+ assert_equal(railties.index('acts_as_yaffle') > railties.index('bukkits'), yaffle_migration_order > bukkits_migration_order)
migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length
output = `bundle exec rake railties:install:migrations`