aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile3
-rw-r--r--README.rdoc5
-rwxr-xr-xRakefile3
-rw-r--r--actionpack/CHANGELOG32
-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/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.rake50
-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/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/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.rb1
-rw-r--r--activemodel/lib/active_model/secure_password.rb11
-rw-r--r--activemodel/test/cases/errors_test.rb6
-rw-r--r--activerecord/CHANGELOG6
-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/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/reflection.rb2
-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.rb16
-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.rb2
-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
-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.textile4
-rw-r--r--railties/guides/source/asset_pipeline.textile330
-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.textile14
-rw-r--r--railties/guides/source/index.html.erb4
-rw-r--r--railties/guides/source/layout.html.erb1
-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.rb11
-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/test/application/asset_debugging_test.rb27
-rw-r--r--railties/test/application/assets_test.rb161
-rw-r--r--railties/test/application/rake_test.rb6
-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
126 files changed, 1969 insertions, 675 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..ac43962d97 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"/>}[https://secure.travis-ci.org/rails/rails.png]
+
== 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..82dfc625a6 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,21 @@
*Rails 3.2.0 (unreleased)*
+* 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 +44,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/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..a8128d9a82 100644
--- a/actionpack/lib/sprockets/assets.rake
+++ b/actionpack/lib/sprockets/assets.rake
@@ -13,35 +13,37 @@ 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)
+ 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/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/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/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..7828434927 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)
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..da109a8738 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]
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 57e4fcf9cf..e82906186e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,4 +1,8 @@
-*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:
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/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/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/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 02a4644cd2..63879259af 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -24,6 +24,7 @@ require 'models/joke'
require 'models/bulb'
require 'rexml/document'
require 'active_support/core_ext/exception'
+require 'bcrypt'
class Category < ActiveRecord::Base; end
class Categorization < ActiveRecord::Base; end
@@ -1865,6 +1866,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)
@@ -1873,6 +1879,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)
@@ -1880,6 +1891,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 9f129cf9fc..b491d63047 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1146,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 693bd0592e..990d9a38dd 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..0bbd3ab5f5 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 = @person.load(@books)}
+ end
+
+ def test_load_hash_with_dates_as_keys
+ assert_nothing_raised{person = @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/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 54ce7a25ab..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:
diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile
index 2695ba9ec1..bad1c08747 100644
--- a/railties/guides/source/asset_pipeline.textile
+++ b/railties/guides/source/asset_pipeline.textile
@@ -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
-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.
+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:
-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.
+<plain>
+.class { background-image: url(<%= asset_path 'image.png' %>) }
+</plain>
+
+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>
+
+This writes the path to the particular asset being referenced.
-* +asset_url("rails.png", "image")+ becomes +url(/assets/rails.png)+
-* +asset_path("rails.png", "image")+ becomes +"/assets/rails.png"+
+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.
+In development mode assets are served as separate files in the order they are specified in the manifest file.
-h4. Debugging Assets
-
-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-validate+ Cache-Control HTTP header to reduce request overhead on subsequent requests -- on these the browser gets a 304 (not-modified) response.
-Additionally if the +config.assets.debug+ is set to true you can debug your assets passing the +:debug+ option to the assets tags:
+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.
+
+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>
-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.
+The fingerprinting behavior is controlled by the setting of +config.assets.digest+ setting in Rails (which is +true+ for production, +false+ for everything else).
+
+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.
+
+Compiled assets are written to the location specified in +config.assets.prefix+. The default setting will use the +public/assets+ directory.
-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.
+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..f4a61482c6 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -61,9 +61,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 +549,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 +592,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
@@ -1604,7 +1604,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 b7813086d7..214155c088 100644
--- a/railties/guides/source/index.html.erb
+++ b/railties/guides/source/index.html.erb
@@ -174,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 5f8ee57517..3ccbc3a477 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -84,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>
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..0ca664e4f0 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -41,11 +41,12 @@ module Rails
@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/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/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..b9d8c002d7 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,180 @@ module ApplicationTests
end
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..0e03c3dc2d 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -79,9 +79,9 @@ 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
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`