aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionpack/CHANGELOG.md10
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb4
-rw-r--r--actionpack/lib/abstract_controller/url_for.rb12
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb2
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb19
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb13
-rw-r--r--actionpack/lib/action_controller/metal/session_management.rb9
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb43
-rw-r--r--actionpack/lib/action_controller/railtie.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb14
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/request_id.rb5
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb11
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb68
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb7
-rw-r--r--actionpack/lib/action_view/asset_paths.rb2
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/tags/base.rb5
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb23
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb49
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb9
-rw-r--r--actionpack/test/abstract/layouts_test.rb12
-rw-r--r--actionpack/test/controller/base_test.rb8
-rw-r--r--actionpack/test/controller/layout_test.rb13
-rw-r--r--actionpack/test/controller/mime_responds_test.rb44
-rw-r--r--actionpack/test/controller/render_test.rb8
-rw-r--r--actionpack/test/controller/routing_test.rb15
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb6
-rw-r--r--actionpack/test/dispatch/routing_assertions_test.rb12
-rw-r--r--actionpack/test/dispatch/uploaded_file_test.rb2
-rw-r--r--actionpack/test/fixtures/test/_b_layout_for_partial.html.erb1
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb102
-rw-r--r--actionpack/test/template/form_options_helper_test.rb9
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb10
-rw-r--r--actionpack/test/template/render_test.rb4
-rw-r--r--actionpack/test/template/sprockets_helper_test.rb1
-rw-r--r--actionpack/test/template/sprockets_helper_with_routes_test.rb4
-rw-r--r--actionpack/test/ts_isolated.rb1
-rw-r--r--activemodel/CHANGELOG.md4
-rw-r--r--activemodel/README.rdoc19
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/model.rb76
-rw-r--r--activemodel/test/cases/model_test.rb26
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/associations/association.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb5
-rw-r--r--activerecord/lib/active_record/autosave_association.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb2
-rw-r--r--activerecord/lib/active_record/core.rb1
-rw-r--r--activerecord/lib/active_record/counter_cache.rb2
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb42
-rw-r--r--activerecord/lib/active_record/dynamic_scope_match.rb7
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb3
-rw-r--r--activerecord/lib/active_record/model_schema.rb25
-rw-r--r--activerecord/lib/active_record/persistence.rb11
-rw-r--r--activerecord/lib/active_record/railties/databases.rake4
-rw-r--r--activerecord/lib/active_record/railties/jdbcmysql_error.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb18
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb27
-rw-r--r--activerecord/lib/active_record/sanitization.rb8
-rw-r--r--activerecord/lib/active_record/validations.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/model.rb5
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb10
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb11
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb43
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb9
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb22
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb11
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb16
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb6
-rw-r--r--activerecord/test/cases/base_test.rb33
-rw-r--r--activerecord/test/cases/multiple_db_test.rb16
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb4
-rw-r--r--activerecord/test/cases/validations/i18n_generate_message_validation_test.rb19
-rw-r--r--activeresource/CHANGELOG.md4
-rw-r--r--activesupport/CHANGELOG.md4
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb36
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb7
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb50
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb42
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb1
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb1
-rw-r--r--activesupport/lib/active_support/xml_mini/libxmlsax.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb1
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb1
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb2
-rw-r--r--activesupport/test/core_ext/object/to_query_test.rb4
-rw-r--r--activesupport/test/lazy_load_hooks_test.rb29
-rw-r--r--activesupport/test/safe_buffer_test.rb38
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/guides/code/getting_started/config/database.yml12
-rw-r--r--railties/guides/source/3_2_release_notes.textile12
-rw-r--r--railties/guides/source/action_view_overview.textile4
-rw-r--r--railties/guides/source/api_app.textile276
-rw-r--r--railties/guides/source/asset_pipeline.textile9
-rw-r--r--railties/guides/source/command_line.textile4
-rw-r--r--railties/guides/source/configuring.textile8
-rw-r--r--railties/guides/source/engines.textile2
-rw-r--r--railties/guides/source/getting_started.textile12
-rw-r--r--railties/guides/source/i18n.textile2
-rw-r--r--railties/guides/source/routing.textile1
-rw-r--r--railties/guides/source/upgrading_ruby_on_rails.textile54
-rw-r--r--railties/lib/rails/application.rb16
-rw-r--r--railties/lib/rails/application/route_inspector.rb2
-rw-r--r--railties/lib/rails/configuration.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml12
-rw-r--r--railties/test/application/loading_test.rb1
-rw-r--r--railties/test/application/middleware/sendfile_test.rb13
-rw-r--r--railties/test/application/middleware_test.rb32
-rw-r--r--railties/test/generators/app_generator_test.rb6
-rw-r--r--railties/test/generators/model_generator_test.rb10
-rw-r--r--railties/test/isolation/abstract_unit.rb5
135 files changed, 1502 insertions, 421 deletions
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index c605f1ff04..f03240acb1 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -17,5 +17,5 @@ Gem::Specification.new do |s|
s.requirements << 'none'
s.add_dependency('actionpack', version)
- s.add_dependency('mail', '~> 2.4.1')
+ s.add_dependency('mail', '~> 2.4.3')
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 74a8d9f78a..78ac05389c 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,9 @@
## Rails 4.0.0 (unreleased) ##
+* Adds support for layouts when rendering a partial with a given collection. *serabe*
+
+* Allows the route helper `root` to take a string argument. For example, `root 'pages#main'`. *bcardarella*
+
* Forms of persisted records use always PATCH (via the `_method` hack). *fxn*
* For resources, both PATCH and PUT are routed to the `update` action. *fxn*
@@ -111,7 +115,7 @@
HTML5 `mark` element. *Brian Cardarella*
-## Rails 3.2.2 (unreleased) ##
+## Rails 3.2.2 (March 1, 2012) ##
* Format lookup for partials is derived from the format in which the template is being rendered. Closes #5025 part 2 *Santiago Pastorino*
@@ -301,7 +305,7 @@
returned by the class method attribute_names will be wrapped. This fixes
the wrapping of nested attributes by adding them to attr_accessible.
-## Rails 3.1.4 (unreleased) ##
+## Rails 3.1.4 (March 1, 2012) ##
* Skip assets group in Gemfile and all assets configurations options
when the application is generated with --skip-sprockets option.
@@ -603,7 +607,7 @@
* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. *Yehuda Katz, Carl Lerche*
-## Rails 3.0.12 (unreleased) ##
+## Rails 3.0.12 (March 1, 2012) ##
* Fix using `tranlate` helper with a html translation which uses the `:count` option for
pluralization.
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index b205acdb79..089913be3c 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', version)
s.add_dependency('activemodel', version)
- s.add_dependency('rack-cache', '~> 1.1')
+ s.add_dependency('rack-cache', '~> 1.2')
s.add_dependency('builder', '~> 3.0.0')
s.add_dependency('rack', '~> 1.4.1')
s.add_dependency('rack-test', '~> 0.6.1')
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index aa39e21ba9..92e93cbee7 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -280,6 +280,10 @@ module AbstractController
<<-RUBY
lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super
RUBY
+ else
+ <<-RUBY
+ super
+ RUBY
end
layout_definition = case _layout
diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb
index e4261068d8..4a95e1f276 100644
--- a/actionpack/lib/abstract_controller/url_for.rb
+++ b/actionpack/lib/abstract_controller/url_for.rb
@@ -1,10 +1,10 @@
-# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
-# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
-# exception will be raised.
-#
-# Note that this module is completely decoupled from HTTP - the only requirement is a valid
-# <tt>_routes</tt> implementation.
module AbstractController
+ # Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
+ # has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
+ # exception will be raised.
+ #
+ # Note that this module is completely decoupled from HTTP - the only requirement is a valid
+ # <tt>_routes</tt> implementation.
module UrlFor
extend ActiveSupport::Concern
include ActionDispatch::Routing::UrlFor
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index a0c54dbd84..7c10fcbb8a 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -31,7 +31,6 @@ module ActionController
autoload :RequestForgeryProtection
autoload :Rescue
autoload :Responder
- autoload :SessionManagement
autoload :Streaming
autoload :Testing
autoload :UrlFor
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 3b82231b15..e20680ef35 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -192,7 +192,6 @@ module ActionController
Renderers::All,
ConditionalGet,
RackDelegation,
- SessionManagement,
Caching,
MimeResponds,
ImplicitRender,
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 2a8e4c575e..5b25a0d303 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -110,8 +110,8 @@ module ActionController
#
# Examples:
# expires_in 20.minutes
+ # expires_in 3.hours, :public => true
# expires_in 3.hours, :public => true, :must_revalidate => true
- # expires_in 3.hours, 'max-stale' => 5.hours, :public => true
#
# This method will overwrite an existing Cache-Control header.
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index b8b43ea9ef..44d2f740e6 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -263,7 +263,7 @@ module ActionController
# The quality of the implementation depends on a good choice.
# A nonce might, for example, be constructed as the base 64 encoding of
#
- # => time-stamp H(time-stamp ":" ETag ":" private-key)
+ # time-stamp H(time-stamp ":" ETag ":" private-key)
#
# where time-stamp is a server-generated time or other non-repeating value,
# ETag is the value of the HTTP ETag header associated with the requested entity,
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 55de7e7d8e..800752975c 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -6,8 +6,6 @@ module ActionController #:nodoc:
module MimeResponds
extend ActiveSupport::Concern
- include ActionController::ImplicitRender
-
included do
class_attribute :responder, :mimes_for_respond_to
self.responder = ActionController::Responder
@@ -58,7 +56,7 @@ module ActionController #:nodoc:
# Clear all mime types in <tt>respond_to</tt>.
#
def clear_respond_to
- self.mimes_for_respond_to = ActiveSupport::OrderedHash.new.freeze
+ self.mimes_for_respond_to = Hash.new.freeze
end
end
@@ -193,7 +191,7 @@ module ActionController #:nodoc:
if collector = retrieve_collector_from_mimes(mimes, &block)
response = collector.response
- response ? response.call : default_render({})
+ response ? response.call : render({})
end
end
@@ -235,16 +233,8 @@ module ActionController #:nodoc:
if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
-
- if defined_response = collector.response
- if action = options.delete(:action)
- render :action => action
- else
- defined_response.call
- end
- else
- (options.delete(:responder) || self.class.responder).call(self, resources, options)
- end
+ options[:default_response] = collector.response
+ (options.delete(:responder) || self.class.responder).call(self, resources, options)
end
end
@@ -281,6 +271,7 @@ module ActionController #:nodoc:
if format
self.content_type ||= format.to_s
lookup_context.formats = [format.to_sym]
+ lookup_context.rendered_format = lookup_context.formats.first
collector
else
head :not_acceptable
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index ccda01ed44..1e8990495c 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -130,6 +130,7 @@ module ActionController #:nodoc:
@resources = resources
@options = options
@action = options.delete(:action)
+ @default_response = options.delete(:default_response)
end
delegate :head, :render, :redirect_to, :to => :controller
@@ -172,7 +173,7 @@ module ActionController #:nodoc:
# responds to :to_format and display it.
#
def to_format
- if get? || !has_errors?
+ if get? || !has_errors? || response_overridden?
default_render
else
display_errors
@@ -226,7 +227,11 @@ module ActionController #:nodoc:
# controller.
#
def default_render
- controller.default_render(options)
+ if @default_response
+ @default_response.call(options)
+ else
+ controller.default_render(options)
+ end
end
# Display is just a shortcut to render a resource with the current format.
@@ -274,5 +279,9 @@ module ActionController #:nodoc:
def json_resource_errors
{:errors => resource.errors}
end
+
+ def response_overridden?
+ @default_response.present?
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/session_management.rb b/actionpack/lib/action_controller/metal/session_management.rb
deleted file mode 100644
index 91d89ff9a4..0000000000
--- a/actionpack/lib/action_controller/metal/session_management.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ActionController #:nodoc:
- module SessionManagement #:nodoc:
- extend ActiveSupport::Concern
-
- module ClassMethods
-
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 0b40b1fc4c..8e7b56dbcc 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -1,25 +1,25 @@
-# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
-# the <tt>_routes</tt> method. Otherwise, an exception will be raised.
-#
-# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
-# url options like the +host+. In order to do so, this module requires the host class
-# to implement +env+ and +request+, which need to be a Rack-compatible.
-#
-# Example:
-#
-# class RootUrl
-# include ActionController::UrlFor
-# include Rails.application.routes.url_helpers
-#
-# delegate :env, :request, :to => :controller
-#
-# def initialize(controller)
-# @controller = controller
-# @url = root_path # named route from the application.
-# end
-# end
-#
module ActionController
+ # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
+ # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
+ #
+ # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
+ # url options like the +host+. In order to do so, this module requires the host class
+ # to implement +env+ and +request+, which need to be a Rack-compatible.
+ #
+ # Example:
+ #
+ # class RootUrl
+ # include ActionController::UrlFor
+ # include Rails.application.routes.url_helpers
+ #
+ # delegate :env, :request, :to => :controller
+ #
+ # def initialize(controller)
+ # @controller = controller
+ # @url = root_path # named route from the application.
+ # end
+ # end
+ #
module UrlFor
extend ActiveSupport::Concern
@@ -42,6 +42,5 @@ module ActionController
@_url_options
end
end
-
end
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index a288e69649..3e170d7872 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -6,7 +6,7 @@ require "abstract_controller/railties/routes_helpers"
require "action_controller/railties/paths"
module ActionController
- class Railtie < Rails::Railtie
+ class Railtie < Rails::Railtie #:nodoc:
config.action_controller = ActiveSupport::OrderedOptions.new
initializer "action_controller.logger" do
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 3509e74d5e..e9a3ec5a22 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -69,6 +69,10 @@ module ActionController
# assert_template :partial => '_customer', :locals => { :customer => @customer }
#
def assert_template(options = {}, message = nil)
+ # Force body to be read in case the
+ # template is being streamed
+ response.body
+
case options
when NilClass, String, Symbol
options = options.to_s if Symbol === options
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 2152351703..e039eb1288 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -82,7 +82,7 @@ module Mime
class << self
TRAILING_STAR_REGEXP = /(text|application)\/\*/
- Q_SEPARATOR_REGEXP = /;\s*q=/
+ PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
def lookup(string)
LOOKUP[string]
@@ -109,7 +109,7 @@ module Mime
def parse(accept_header)
if accept_header !~ /,/
- accept_header = accept_header.split(Q_SEPARATOR_REGEXP).first
+ accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
if accept_header =~ TRAILING_STAR_REGEXP
parse_data_with_trailing_star($1)
else
@@ -119,7 +119,7 @@ module Mime
# keep track of creation order to keep the subsequent sort stable
list, index = [], 0
accept_header.split(/,/).each do |header|
- params, q = header.split(Q_SEPARATOR_REGEXP)
+ params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
if params.present?
params.strip!
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 5ab99d1061..c4a915d1ad 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -4,11 +4,12 @@ module ActionDispatch
attr_accessor :original_filename, :content_type, :tempfile, :headers
def initialize(hash)
+ @tempfile = hash[:tempfile]
+ raise(ArgumentError, ':tempfile is required') unless @tempfile
+
@original_filename = encode_filename(hash[:filename])
@content_type = hash[:type]
@headers = hash[:head]
- @tempfile = hash[:tempfile]
- raise(ArgumentError, ':tempfile is required') unless @tempfile
end
def read(*args)
@@ -19,15 +20,12 @@ module ActionDispatch
[:open, :path, :rewind, :size].each do |method|
class_eval "def #{method}; @tempfile.#{method}; end"
end
-
+
private
+
def encode_filename(filename)
# Encode the filename in the utf8 encoding, unless it is nil
- if filename
- filename.force_encoding("UTF-8").encode!
- else
- filename
- end
+ filename.force_encoding("UTF-8").encode! if filename
end
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 80ffbe575b..f9dae5dad7 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -40,7 +40,9 @@ module ActionDispatch
rewritten_url << ":#{options.delete(:port)}" if options[:port]
end
- path = options.delete(:path) || ''
+ path = ""
+ path << options.delete(:script_name).to_s.chomp("/")
+ path << options.delete(:path).to_s
params = options[:params] || {}
params.reject! {|k,v| v.to_param.nil? }
diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb
index d5a0b80fd5..6fff94707c 100644
--- a/actionpack/lib/action_dispatch/middleware/request_id.rb
+++ b/actionpack/lib/action_dispatch/middleware/request_id.rb
@@ -19,10 +19,7 @@ module ActionDispatch
def call(env)
env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
- status, headers, body = @app.call(env)
-
- headers["X-Request-Id"] = env["action_dispatch.request_id"]
- [ status, headers, body ]
+ @app.call(env).tap { |status, headers, body| headers["X-Request-Id"] = env["action_dispatch.request_id"] }
end
private
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 35f901c575..62f906219c 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -16,7 +16,7 @@ module ActionDispatch
config.action_dispatch.rack_cache = {
:metastore => "rails:/",
:entitystore => "rails:/",
- :verbose => true
+ :verbose => false
}
initializer "action_dispatch.configure" do |app|
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 02a27110e4..cd215034dc 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -254,10 +254,15 @@ module ActionDispatch
#
# For options, see +match+, as +root+ uses it internally.
#
+ # You can also pass a string which will expand
+ #
+ # root 'pages#main'
+ #
# You should put the root route at the top of <tt>config/routes.rb</tt>,
# because this means it will be matched first. As this is the most popular route
# of most Rails applications, this is beneficial.
def root(options = {})
+ options = { :to => options } if options.is_a?(String)
match '/', { :as => :root }.merge(options)
end
@@ -446,7 +451,11 @@ module ActionDispatch
_route = @set.named_routes.routes[name.to_sym]
_routes = @set
app.routes.define_mounted_helper(name)
- app.routes.class_eval do
+ app.routes.singleton_class.class_eval do
+ define_method :mounted? do
+ true
+ end
+
define_method :_generate_prefix do |options|
prefix_options = options.slice(*_route.segment_keys)
# we must actually delete prefix segment keys to avoid passing them to next url_for
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 57c6972078..30e9e5634b 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -191,14 +191,50 @@ module ActionDispatch
selector = url_helper_name(name, kind)
hash_access_method = hash_access_name(name, kind)
- @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
- remove_possible_method :#{selector}
- def #{selector}(*args)
- url_for(#{hash_access_method}(*args))
- end
- END_EVAL
+ if optimize_helper?(route)
+ @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
+ remove_possible_method :#{selector}
+ def #{selector}(*args)
+ if args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation?
+ options = #{options.inspect}.merge!(url_options)
+ options[:path] = "#{optimized_helper(route)}"
+ ActionDispatch::Http::URL.url_for(options)
+ else
+ url_for(#{hash_access_method}(*args))
+ end
+ end
+ END_EVAL
+ else
+ @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
+ remove_possible_method :#{selector}
+ def #{selector}(*args)
+ url_for(#{hash_access_method}(*args))
+ end
+ END_EVAL
+ end
+
helpers << selector
end
+
+ # Clause check about when we need to generate an optimized helper.
+ def optimize_helper?(route) #:nodoc:
+ route.ast.grep(Journey::Nodes::Star).empty? && route.requirements.except(:controller, :action).empty?
+ end
+
+ # Generates the interpolation to be used in the optimized helper.
+ def optimized_helper(route)
+ string_route = route.ast.to_s
+
+ while string_route.gsub!(/\([^\)]*\)/, "")
+ true
+ end
+
+ route.required_parts.each_with_index do |part, i|
+ string_route.gsub!(part.inspect, "\#{Journey::Router::Utils.escape_fragment(args[#{i}].to_param)}")
+ end
+
+ string_route
+ end
end
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
@@ -323,7 +359,7 @@ module ActionDispatch
# Rails.application.routes.url_helpers.url_for(args)
@_routes = routes
class << self
- delegate :url_for, :to => '@_routes'
+ delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
end
# Make named_routes available in the module singleton
@@ -557,6 +593,14 @@ module ActionDispatch
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
:trailing_slash, :anchor, :params, :only_path, :script_name]
+ def mounted?
+ false
+ end
+
+ def optimize_routes_generation?
+ !mounted? && default_url_options.empty?
+ end
+
def _generate_prefix(options = {})
nil
end
@@ -568,19 +612,17 @@ module ActionDispatch
user, password = extract_authentication(options)
path_segments = options.delete(:_path_segments)
- script_name = options.delete(:script_name)
-
- path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
+ script_name = options.delete(:script_name).presence || _generate_prefix(options)
path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?
- path_addition, params = generate(path_options, path_segments || {})
- path << path_addition
+ path, params = generate(path_options, path_segments || {})
params.merge!(options[:params] || {})
ActionDispatch::Http::URL.url_for(options.merge!({
:path => path,
+ :script_name => script_name,
:params => params,
:user => user,
:password => password
@@ -594,6 +636,7 @@ module ActionDispatch
def recognize_path(path, environment = {})
method = (environment[:method] || "GET").to_s.upcase
path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
+ extras = environment[:extras] || {}
begin
env = Rack::MockRequest.env_for(path, {:method => method})
@@ -603,6 +646,7 @@ module ActionDispatch
req = @request_class.new(env)
@router.recognize(req) do |route, matches, params|
+ params.merge!(extras)
params.each do |key, value|
if value.is_a?(String)
value = value.dup.force_encoding(Encoding::BINARY)
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index ee6616c5d3..94db36ce1f 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -102,6 +102,9 @@ module ActionDispatch
super
end
+ # Hook overriden in controller to add request information
+ # with `default_url_options`. Application logic should not
+ # go into url_options.
def url_options
default_url_options
end
@@ -152,6 +155,11 @@ module ActionDispatch
protected
+ def optimize_routes_generation?
+ return @_optimized_routes if defined?(@_optimized_routes)
+ @_optimized_routes = _routes.optimize_routes_generation? && default_url_options.empty?
+ end
+
def _with_routes(routes)
old_routes, @_routes = @_routes, routes
yield
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 1552676fbb..1f4b905d18 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -39,10 +39,9 @@ module ActionDispatch
# # Test a custom route
# assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1')
def assert_recognizes(expected_options, path, extras={}, message=nil)
- request = recognized_request_for(path)
+ request = recognized_request_for(path, extras)
expected_options = expected_options.clone
- extras.each_key { |key| expected_options.delete key } unless extras.nil?
expected_options.stringify_keys!
@@ -181,7 +180,7 @@ module ActionDispatch
private
# Recognizes the route for a given path.
- def recognized_request_for(path)
+ def recognized_request_for(path, extras = {})
if path.is_a?(Hash)
method = path[:method]
path = path[:path]
@@ -209,7 +208,7 @@ module ActionDispatch
request.request_method = method if method
- params = @routes.recognize_path(path, { :method => method })
+ params = @routes.recognize_path(path, { :method => method, :extras => extras })
request.path_parameters = params.with_indifferent_access
request
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb
index 2a28e780bf..add8d94b70 100644
--- a/actionpack/lib/action_view/asset_paths.rb
+++ b/actionpack/lib/action_view/asset_paths.rb
@@ -117,7 +117,7 @@ module ActionView
end
def relative_url_root
- config.relative_url_root
+ config.relative_url_root || current_request.try(:script_name)
end
def asset_host_config
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 17bbfe2efd..278139cadb 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -215,7 +215,7 @@ module ActionView
def flush_output_buffer #:nodoc:
if output_buffer && !output_buffer.empty?
response.body_parts << output_buffer
- self.output_buffer = output_buffer[0,0]
+ self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
nil
end
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 53ae8b66da..7492ef56c6 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -1016,7 +1016,7 @@ module ActionView
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options[:builder] ||= options[:builder]
fields_options[:parent_builder] = self
- fields_options[:namespace] = fields_options[:parent_builder].options[:namespace]
+ fields_options[:namespace] = options[:namespace]
case record_name
when String, Symbol
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 2cf1c9055c..9fad30a48f 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -525,10 +525,9 @@ module ActionView
# <% end %>
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
def field_set_tag(legend = nil, options = nil, &block)
- content = capture(&block)
output = tag(:fieldset, options, true)
output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
- output.concat(content)
+ output.concat(capture(&block)) if block_given?
output.safe_concat("</fieldset>")
end
diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb
index c9c891daa1..22c16de057 100644
--- a/actionpack/lib/action_view/helpers/tags/base.rb
+++ b/actionpack/lib/action_view/helpers/tags/base.rb
@@ -133,12 +133,11 @@ module ActionView
def add_options(option_tags, options, value = nil)
if options[:include_blank]
- include_blank = options[:include_blank] if options[:include_blank].kind_of?(String)
- option_tags = content_tag(:option, include_blank, :value => '').safe_concat("\n").safe_concat(option_tags)
+ option_tags = content_tag('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
end
if value.blank? && options[:prompt]
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
- option_tags = content_tag(:option, prompt, :value => '').safe_concat("\n").safe_concat(option_tags)
+ option_tags = content_tag('option', prompt, :value => '') + "\n" + option_tags
end
option_tags
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 8d7417809b..f4946e65b5 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -23,20 +23,25 @@ module ActionView
include ActionDispatch::Routing::UrlFor
include TagHelper
- def _routes_context
- controller
- end
+ # We need to override url_optoins, _routes_context
+ # and optimize_routes_generation? to consider the controller.
- # Need to map default url options to controller one.
- # def default_url_options(*args) #:nodoc:
- # controller.send(:default_url_options, *args)
- # end
- #
- def url_options
+ def url_options #:nodoc:
return super unless controller.respond_to?(:url_options)
controller.url_options
end
+ def _routes_context #:nodoc:
+ controller
+ end
+ protected :_routes_context
+
+ def optimize_routes_generation? #:nodoc:
+ controller.respond_to?(:optimize_routes_generation?) ?
+ controller.optimize_routes_generation? : super
+ end
+ protected :optimize_routes_generation?
+
# Returns the URL for the set of +options+ provided. This takes the
# same options as +url_for+ in Action Controller (see the
# documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 3033294883..245a19deec 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -158,6 +158,43 @@ module ActionView
# Name: <%= user.name %>
# </div>
#
+ # If a collection is given, the layout will be rendered once for each item in the collection. Just think
+ # these two snippets have the same output:
+ #
+ # <%# app/views/users/_user.html.erb %>
+ # Name: <%= user.name %>
+ #
+ # <%# app/views/users/index.html.erb %>
+ # <%# This does not use layouts %>
+ # <ul>
+ # <% users.each do |user| -%>
+ # <li>
+ # <%= render :partial => "user", :locals => { :user => user } %>
+ # </li>
+ # <% end -%>
+ # </ul>
+ #
+ # <%# app/views/users/_li_layout.html.erb %>
+ # <li>
+ # <%= yield %>
+ # </li>
+ #
+ # <%# app/views/users/index.html.erb %>
+ # <ul>
+ # <%= render :partial => "user", :layout => "li_layout", :collection => users %>
+ # </ul>
+ #
+ # Given two users whose names are Alice and Bob, these snippets return:
+ #
+ # <ul>
+ # <li>
+ # Name: Alice
+ # </li>
+ # <li>
+ # Name: Bob
+ # </li>
+ # </ul>
+ #
# You can also apply a layout to a block within any template:
#
# <%# app/views/users/_chief.html.erb &>
@@ -238,7 +275,14 @@ module ActionView
spacer = find_template(@options[:spacer_template]).render(@view, @locals)
end
+ if layout = @options[:layout]
+ layout = find_template(layout)
+ end
+
result = @template ? collection_with_template : collection_without_template
+
+ result.map!{|content| layout.render(@view, @locals) { content } } if layout
+
result.join(spacer).html_safe
end
@@ -272,6 +316,8 @@ module ActionView
@block = block
@details = extract_details(options)
+ @lookup_context.rendered_format ||= formats.first
+
if String === partial
@object = options[:object]
@path = partial
@@ -340,9 +386,10 @@ module ActionView
locals[as] = object
segments << template.render(@view, locals)
end
-
+
segments
end
+
def collection_without_template
segments, locals, collection_data = [], @locals, @collection_data
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 20f75fba45..f7df9a6322 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -6,8 +6,13 @@ module ActionView
@view = context
@details = extract_details(options)
template = determine_template(options)
- @lookup_context.rendered_format ||= template.formats.first
- @lookup_context.formats = template.formats
+ context = @lookup_context
+
+ unless context.rendered_format
+ context.rendered_format = template.formats.first
+ context.formats = template.formats
+ end
+
render_template(template, options[:layout], options[:locals])
end
diff --git a/actionpack/test/abstract/layouts_test.rb b/actionpack/test/abstract/layouts_test.rb
index fc25718d9e..e07a6de4a9 100644
--- a/actionpack/test/abstract/layouts_test.rb
+++ b/actionpack/test/abstract/layouts_test.rb
@@ -299,6 +299,18 @@ module AbstractControllerTests
controller.process(:index)
assert_equal "Overwrite Hello index!", controller.response_body
end
+
+ test "layout for anonymous controller" do
+ klass = Class.new(WithString) do
+ def index
+ render :text => 'index', :layout => true
+ end
+ end
+
+ controller = klass.new
+ controller.process(:index)
+ assert_equal "With String index", controller.response_body
+ end
end
end
end
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 791edb9069..7d0609751f 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -56,7 +56,7 @@ class UrlOptionsController < ActionController::Base
end
def url_options
- super.merge(:host => 'www.override.com', :action => 'new', :locale => 'en')
+ super.merge(:host => 'www.override.com')
end
end
@@ -183,9 +183,9 @@ class UrlOptionsTest < ActionController::TestCase
get :from_view, :route => "from_view_url"
- assert_equal 'http://www.override.com/from_view?locale=en', @response.body
- assert_equal 'http://www.override.com/from_view?locale=en', @controller.send(:from_view_url)
- assert_equal 'http://www.override.com/default_url_options/new?locale=en', @controller.url_for(:controller => 'default_url_options')
+ assert_equal 'http://www.override.com/from_view', @response.body
+ assert_equal 'http://www.override.com/from_view', @controller.send(:from_view_url)
+ assert_equal 'http://www.override.com/default_url_options/index', @controller.url_for(:controller => 'default_url_options')
end
end
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index bc171e201b..c73b36f05e 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -78,6 +78,13 @@ end
class DefaultLayoutController < LayoutTest
end
+class StreamingLayoutController < LayoutTest
+ def render(*args)
+ options = args.extract_options! || {}
+ super(*args, options.merge(:stream => true))
+ end
+end
+
class AbsolutePathLayoutController < LayoutTest
layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test')
end
@@ -122,6 +129,12 @@ class LayoutSetInResponseTest < ActionController::TestCase
assert_template :layout => "layouts/layout_test"
end
+ def test_layout_set_when_using_streaming_layout
+ @controller = StreamingLayoutController.new
+ get :hello
+ assert_template :hello
+ end
+
def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index ae368842b5..0e4099ddc6 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -132,7 +132,6 @@ class RespondToController < ActionController::Base
end
end
-
def iphone_with_html_response_type
request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone"
@@ -1135,7 +1134,7 @@ class PostController < AbstractPostController
around_filter :with_iphone
def index
- respond_to(:html, :iphone)
+ respond_to(:html, :iphone, :js)
end
protected
@@ -1182,4 +1181,45 @@ class MimeControllerLayoutsTest < ActionController::TestCase
get :index
assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
end
+
+ def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout
+ get :index, :format => :js
+ assert_equal "Hello Firefox", @response.body
+ end
+end
+
+class FlashResponder < ActionController::Responder
+ def initialize(controller, resources, options={})
+ super
+ end
+
+ def to_html
+ controller.flash[:notice] = 'Success'
+ super
+ end
+end
+
+class FlashResponderController < ActionController::Base
+ self.responder = FlashResponder
+ respond_to :html
+
+ def index
+ respond_with Object.new do |format|
+ format.html { render :text => 'HTML' }
+ end
+ end
+end
+
+class FlashResponderControllerTest < ActionController::TestCase
+ tests FlashResponderController
+
+ def test_respond_with_block_executed
+ get :index
+ assert_equal 'HTML', @response.body
+ end
+
+ def test_flash_responder_executed
+ get :index
+ assert_equal 'Success', flash[:notice]
+ end
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index e040878b26..8167fc2fd2 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -1246,22 +1246,26 @@ class RenderTest < ActionController::TestCase
def test_partial_only
get :partial_only
assert_equal "only partial", @response.body
+ assert_equal "text/html", @response.content_type
end
def test_should_render_html_formatted_partial
get :partial
- assert_equal 'partial html', @response.body
+ assert_equal "partial html", @response.body
+ assert_equal "text/html", @response.content_type
end
def test_should_render_html_partial_with_formats
get :partial_formats_html
- assert_equal 'partial html', @response.body
+ assert_equal "partial html", @response.body
+ assert_equal "text/html", @response.content_type
end
def test_render_to_string_partial
get :render_to_string_with_partial
assert_equal "only partial", assigns(:partial_only)
assert_equal "Hello: david", assigns(:partial_with_locals)
+ assert_equal "text/html", @response.content_type
end
def test_partial_with_counter
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 843ae1a813..272a7da8c5 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -59,11 +59,11 @@ end
class MockController
def self.build(helpers)
Class.new do
- def url_for(options)
+ def url_options
+ options = super
options[:protocol] ||= "http"
options[:host] ||= "test.host"
-
- super(options)
+ options
end
include helpers
@@ -437,6 +437,15 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
assert_equal("/", routes.send(:root_path))
end
+ def test_named_route_root_without_hash
+ rs.draw do
+ root "hello#index"
+ end
+ routes = setup_for_named_route
+ assert_equal("http://test.host/", routes.send(:root_url))
+ assert_equal("/", routes.send(:root_path))
+ end
+
def test_named_route_with_regexps
rs.draw do
match 'page/:year/:month/:day/:title' => 'page#show', :as => 'article',
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 0372c42920..e3f9faaa64 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -75,6 +75,12 @@ class MimeTypeTest < ActiveSupport::TestCase
assert_equal expect, Mime::Type.parse(accept)
end
+ test "parse arbitarry media type parameters" do
+ accept = 'multipart/form-data; boundary="simple boundary"'
+ expect = [Mime::MULTIPART_FORM]
+ assert_equal expect, Mime::Type.parse(accept)
+ end
+
# Accept header send with user HTTP_USER_AGENT: Sunrise/0.42j (Windows XP)
test "parse broken acceptlines" do
accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/*,,*/*;q=0.5"
diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb
index 9f95d82129..e953029456 100644
--- a/actionpack/test/dispatch/routing_assertions_test.rb
+++ b/actionpack/test/dispatch/routing_assertions_test.rb
@@ -3,6 +3,7 @@ require 'controller/fake_controllers'
class SecureArticlesController < ArticlesController; end
class BlockArticlesController < ArticlesController; end
+class QueryArticlesController < ArticlesController; end
class RoutingAssertionsTest < ActionController::TestCase
@@ -18,6 +19,10 @@ class RoutingAssertionsTest < ActionController::TestCase
scope 'block', :constraints => lambda { |r| r.ssl? } do
resources :articles, :controller => 'block_articles'
end
+
+ scope 'query', :constraints => lambda { |r| r.params[:use_query] == 'true' } do
+ resources :articles, :controller => 'query_articles'
+ end
end
end
@@ -62,6 +67,13 @@ class RoutingAssertionsTest < ActionController::TestCase
assert_recognizes({ :controller => 'block_articles', :action => 'index' }, 'https://test.host/block/articles')
end
+ def test_assert_recognizes_with_query_constraint
+ assert_raise(ActionController::RoutingError) do
+ assert_recognizes({ :controller => 'query_articles', :action => 'index', :use_query => 'false' }, '/query/articles', { :use_query => 'false' })
+ end
+ assert_recognizes({ :controller => 'query_articles', :action => 'index', :use_query => 'true' }, '/query/articles', { :use_query => 'true' })
+ end
+
def test_assert_routing
assert_routing('/articles', :controller => 'articles', :action => 'index')
end
diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb
index 0b95291e18..1e3f720fa7 100644
--- a/actionpack/test/dispatch/uploaded_file_test.rb
+++ b/actionpack/test/dispatch/uploaded_file_test.rb
@@ -12,7 +12,7 @@ module ActionDispatch
uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
assert_equal 'foo', uf.original_filename
end
-
+
def test_filename_should_be_in_utf_8
uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
assert_equal "UTF-8", uf.original_filename.encoding.to_s
diff --git a/actionpack/test/fixtures/test/_b_layout_for_partial.html.erb b/actionpack/test/fixtures/test/_b_layout_for_partial.html.erb
new file mode 100644
index 0000000000..e918ba8f83
--- /dev/null
+++ b/actionpack/test/fixtures/test/_b_layout_for_partial.html.erb
@@ -0,0 +1 @@
+<b><%= yield %></b> \ No newline at end of file
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 3d2eeea503..58ff055fc2 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -38,6 +38,7 @@ class AssetTagHelperTest < ActionView::TestCase
@controller = BasicController.new
@request = Class.new do
+ attr_accessor :script_name
def protocol() 'http://' end
def ssl?() false end
def host_with_port() 'localhost' end
@@ -647,6 +648,13 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal %(<img alt="Rails" src="#{@controller.config.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
end
+ # Same as above, but with script_name
+ def test_timebased_asset_id_with_script_name
+ @request.script_name = "/collaboration/hieraki"
+ expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
+ assert_equal %(<img alt="Rails" src="#{@request.script_name}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
+ end
+
def test_should_skip_asset_id_on_complete_url
assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png")
end
@@ -897,6 +905,31 @@ class AssetTagHelperTest < ActionView::TestCase
FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
end
+ # Same as above, but with script_name
+ def test_caching_javascript_include_tag_with_script_name
+ ENV["RAILS_ASSET_ID"] = ""
+ @request.script_name = "/collaboration/hieraki"
+ config.perform_caching = true
+
+ assert_dom_equal(
+ %(<script src="/collaboration/hieraki/javascripts/all.js" type="text/javascript"></script>),
+ javascript_include_tag(:all, :cache => true)
+ )
+
+ assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+
+ assert_dom_equal(
+ %(<script src="/collaboration/hieraki/javascripts/money.js" type="text/javascript"></script>),
+ javascript_include_tag(:all, :cache => "money")
+ )
+
+ assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+
+ ensure
+ FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+ FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+ end
+
def test_caching_javascript_include_tag_with_named_paths_and_relative_url_root_when_caching_off
ENV["RAILS_ASSET_ID"] = ""
@controller.config.relative_url_root = "/collaboration/hieraki"
@@ -917,6 +950,27 @@ class AssetTagHelperTest < ActionView::TestCase
assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
end
+ # Same as above, but with script_name
+ def test_caching_javascript_include_tag_with_named_paths_and_script_name_when_caching_off
+ ENV["RAILS_ASSET_ID"] = ""
+ @request.script_name = "/collaboration/hieraki"
+ config.perform_caching = false
+
+ assert_dom_equal(
+ %(<script src="/collaboration/hieraki/javascripts/robber.js" type="text/javascript"></script>),
+ javascript_include_tag('robber', :cache => true)
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+
+ assert_dom_equal(
+ %(<script src="/collaboration/hieraki/javascripts/robber.js" type="text/javascript"></script>),
+ javascript_include_tag('robber', :cache => "money", :recursive => true)
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+ end
+
def test_caching_javascript_include_tag_when_caching_off
ENV["RAILS_ASSET_ID"] = ""
config.perform_caching = false
@@ -1144,6 +1198,33 @@ class AssetTagHelperTest < ActionView::TestCase
FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
end
+ # Same as above, but with script_name
+ def test_caching_stylesheet_link_tag_with_script_name
+ ENV["RAILS_ASSET_ID"] = ""
+ @request.script_name = "/collaboration/hieraki"
+ config.perform_caching = true
+
+ assert_dom_equal(
+ %(<link href="/collaboration/hieraki/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />),
+ stylesheet_link_tag(:all, :cache => true)
+ )
+
+ files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"]
+
+ expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max
+ assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+
+ assert_dom_equal(
+ %(<link href="/collaboration/hieraki/stylesheets/money.css" media="screen" rel="stylesheet" type="text/css" />),
+ stylesheet_link_tag(:all, :cache => "money")
+ )
+
+ assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+ ensure
+ FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+ FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+ end
+
def test_caching_stylesheet_link_tag_with_named_paths_and_relative_url_root_when_caching_off
ENV["RAILS_ASSET_ID"] = ""
@@ -1165,6 +1246,27 @@ class AssetTagHelperTest < ActionView::TestCase
assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
end
+ # Same as above, but with script_name
+ def test_caching_stylesheet_link_tag_with_named_paths_and_script_name_when_caching_off
+ ENV["RAILS_ASSET_ID"] = ""
+ @request.script_name = "/collaboration/hieraki"
+ config.perform_caching = false
+
+ assert_dom_equal(
+ %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />),
+ stylesheet_link_tag('robber', :cache => true)
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+
+ assert_dom_equal(
+ %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />),
+ stylesheet_link_tag('robber', :cache => "money")
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+ end
+
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 6b8d62df62..606d454cb3 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -509,7 +509,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_select_under_fields_for_with_string_and_given_prompt
@post = Post.new
- options = "<option value=\"abe\">abe</option><option value=\"mus\">mus</option><option value=\"hest\">hest</option>"
+ options = "<option value=\"abe\">abe</option><option value=\"mus\">mus</option><option value=\"hest\">hest</option>".html_safe
output_buffer = fields_for :post, @post do |f|
concat f.select(:category, options, :prompt => 'The prompt')
@@ -665,6 +665,13 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_select_escapes_options
+ assert_dom_equal(
+ '<select id="post_title" name="post[title]">&lt;script&gt;alert(1)&lt;/script&gt;</select>',
+ select('post', 'title', '<script>alert(1)</script>')
+ )
+ end
+
def test_select_with_selected_nil
@post = Post.new
@post.category = "<mus>"
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 6ef4cf4dd2..590a1967c5 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -513,6 +513,16 @@ class FormTagHelperTest < ActionView::TestCase
expected = %(<fieldset class="format">Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
+
+ output_buffer = render_erb("<%= field_set_tag %>")
+
+ expected = %(<fieldset></fieldset>)
+ assert_dom_equal expected, output_buffer
+
+ output_buffer = render_erb("<%= field_set_tag('You legend!') %>")
+
+ expected = %(<fieldset><legend>You legend!</legend></fieldset>)
+ assert_dom_equal expected, output_buffer
end
def test_text_area_tag_options_symbolize_keys_side_effects
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 7347e15373..122b07d348 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -238,6 +238,10 @@ module RenderTestCases
def test_render_partial_with_nil_values_in_collection
assert_equal "Hello: davidHello: Anonymous", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), nil ])
end
+
+ def test_render_partial_with_layout_using_collection_and_template
+ assert_equal "<b>Hello: Amazon</b><b>Hello: Yahoo</b>", @view.render(:partial => "test/customer", :layout => 'test/b_layout_for_partial', :collection => [ Customer.new("Amazon"), Customer.new("Yahoo") ])
+ end
def test_render_partial_with_empty_array_should_return_nil
assert_nil @view.render(:partial => [])
diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb
index 1c591bdcc2..d4d2dedfb6 100644
--- a/actionpack/test/template/sprockets_helper_test.rb
+++ b/actionpack/test/template/sprockets_helper_test.rb
@@ -12,6 +12,7 @@ class SprocketsHelperTest < ActionView::TestCase
def protocol() 'http://' end
def ssl?() false end
def host_with_port() 'localhost' end
+ def script_name() nil end
end
def setup
diff --git a/actionpack/test/template/sprockets_helper_with_routes_test.rb b/actionpack/test/template/sprockets_helper_with_routes_test.rb
index bcbd81a7dd..89b9940eb7 100644
--- a/actionpack/test/template/sprockets_helper_with_routes_test.rb
+++ b/actionpack/test/template/sprockets_helper_with_routes_test.rb
@@ -17,7 +17,7 @@ class SprocketsHelperWithRoutesTest < ActionView::TestCase
def setup
super
- @controller = BasicController.new
+ @controller = BasicController.new
@assets = Sprockets::Environment.new
@assets.append_path(FIXTURES.join("sprockets/app/javascripts"))
@@ -34,7 +34,7 @@ class SprocketsHelperWithRoutesTest < ActionView::TestCase
test "namespace conflicts on a named route called asset_path" do
# Testing this for sanity - asset_path is now a named route!
- assert_match asset_path('test_asset'), '/assets/test_asset'
+ assert_equal asset_path('test_asset'), '/assets/test_asset'
assert_match %r{/assets/logo-[0-9a-f]+.png},
path_to_asset("logo.png")
diff --git a/actionpack/test/ts_isolated.rb b/actionpack/test/ts_isolated.rb
index 7430de2299..ae2a0c95f6 100644
--- a/actionpack/test/ts_isolated.rb
+++ b/actionpack/test/ts_isolated.rb
@@ -3,7 +3,6 @@ $:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
require 'minitest/autorun'
require 'rbconfig'
-require 'active_support/core_ext/kernel/reporting'
require 'abstract_unit'
class TestIsolated < ActiveSupport::TestCase
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 0d70edd0ba..42adbec8b9 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,3 +1,5 @@
+* Added ActiveModel::Model, a mixin to make Ruby objects work with AP out of box *Guillermo Iguaran*
+
* `AM::Errors#to_json`: support `:full_messages` parameter *Bogdan Gusiev*
* Trim down Active Model API by removing `valid?` and `errors.full_messages` *José Valim*
@@ -61,7 +63,7 @@
* Add support for selectively enabling/disabling observers *Myron Marston*
-## Rails 3.0.12 (unreleased) ##
+## Rails 3.0.12 (March 1, 2012) ##
* No changes.
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index a7ba27ba73..4861b0a002 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -13,6 +13,25 @@ in code duplication and fragile applications that broke on upgrades. Active
Model solves this by defining an explicit API. You can read more about the
API in ActiveModel::Lint::Tests.
+Active Model provides a default module that implements the basic API required
+to integrate with Action Pack out of the box: +ActiveModel::Model+.
+
+ class Person
+ include ActiveModel::Model
+
+ attr_accessor :name, :age
+ validates_presence_of :name
+ end
+
+ person = Person.new(:name => 'bob', :age => '18')
+ person.name # => 'bob'
+ person.age # => 18
+ person.valid? # => false
+
+It includes model name introspections, conversions, translations and
+validations, resulting in a class suitable to be used with Action Pack.
+See +ActiveModel::Model+ for more examples.
+
Active Model also provides the following functionality to have ORM-like
behavior out of the box:
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 85514e63fd..2586147a20 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -39,6 +39,7 @@ module ActiveModel
autoload :Errors
autoload :Lint
autoload :MassAssignmentSecurity
+ autoload :Model
autoload :Name, 'active_model/naming'
autoload :Naming
autoload :Observer, 'active_model/observing'
diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb
new file mode 100644
index 0000000000..6825fdc653
--- /dev/null
+++ b/activemodel/lib/active_model/model.rb
@@ -0,0 +1,76 @@
+module ActiveModel
+
+ # == Active Model Basic Model
+ #
+ # Includes the required interface for an object to interact with +ActionPack+,
+ # using different +ActiveModel+ modules. It includes model name introspections,
+ # conversions, translations and validations. Besides that, it allows you to
+ # initialize the object with a hash of attributes, pretty much like
+ # +ActiveRecord+ does.
+ #
+ # A minimal implementation could be:
+ #
+ # class Person
+ # include ActiveModel::Model
+ # attr_accessor :name, :age
+ # end
+ #
+ # person = Person.new(:name => 'bob', :age => '18')
+ # person.name # => 'bob'
+ # person.age # => 18
+ #
+ # Note that, by default, +ActiveModel::Model+ implements +persisted?+ to
+ # return +false+, which is the most common case. You may want to override it
+ # in your class to simulate a different scenario:
+ #
+ # class Person
+ # include ActiveModel::Model
+ # attr_accessor :id, :name
+ #
+ # def persisted?
+ # self.id == 1
+ # end
+ # end
+ #
+ # person = Person.new(:id => 1, :name => 'bob')
+ # person.persisted? # => true
+ #
+ # Also, if for some reason you need to run code on +initialize+, make sure you
+ # call super if you want the attributes hash initialization to happen.
+ #
+ # class Person
+ # include ActiveModel::Model
+ # attr_accessor :id, :name, :omg
+ #
+ # def initialize(attributes)
+ # super
+ # @omg ||= true
+ # end
+ # end
+ #
+ # person = Person.new(:id => 1, :name => 'bob')
+ # person.omg # => true
+ #
+ # For more detailed information on other functionalities available, please refer
+ # to the specific modules included in +ActiveModel::Model+ (see below).
+ module Model
+ def self.included(base)
+ base.class_eval do
+ extend ActiveModel::Naming
+ extend ActiveModel::Translation
+ include ActiveModel::Validations
+ include ActiveModel::Conversion
+ end
+ end
+
+ def initialize(params={})
+ params.each do |attr, value|
+ self.public_send("#{attr}=", value)
+ end if params
+ end
+
+ def persisted?
+ false
+ end
+ end
+end
diff --git a/activemodel/test/cases/model_test.rb b/activemodel/test/cases/model_test.rb
new file mode 100644
index 0000000000..d93fd96b88
--- /dev/null
+++ b/activemodel/test/cases/model_test.rb
@@ -0,0 +1,26 @@
+require 'cases/helper'
+
+class ModelTest < ActiveModel::TestCase
+ include ActiveModel::Lint::Tests
+
+ class BasicModel
+ include ActiveModel::Model
+ attr_accessor :attr
+ end
+
+ def setup
+ @model = BasicModel.new
+ end
+
+ def test_initialize_with_params
+ object = BasicModel.new(:attr => "value")
+ assert_equal object.attr, "value"
+ end
+
+ def test_initialize_with_nil_or_empty_hash_params_does_not_explode
+ assert_nothing_raised do
+ BasicModel.new()
+ BasicModel.new({})
+ end
+ end
+end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 16f1c589c4..21f2b97f5d 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -343,7 +343,7 @@
*Aaron Christy*
- ## Rails 3.1.4 (unreleased) ##
+## Rails 3.1.4 (March 1, 2012) ##
* Fix a custom primary key regression *GH 3987*
@@ -802,7 +802,7 @@
*Aaron Patterson*
-## Rails 3.0.12 (unreleased) ##
+## Rails 3.0.12 (March 1, 2012) ##
* No changes.
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 7887d59aad..512c52338e 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -28,6 +28,7 @@ module ActiveRecord
@target = nil
@owner, @reflection = owner, reflection
@updated = false
+ @stale_state = nil
reset
reset_scope
@@ -231,7 +232,8 @@ module ActiveRecord
def build_record(attributes, options)
reflection.build_association(attributes, options) do |record|
- record.assign_attributes(create_scope.except(*record.changed), :without_protection => true)
+ attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
+ record.assign_attributes(attributes, :without_protection => true)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 3e27e85f02..93c243e7b1 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -189,7 +189,7 @@ module ActiveRecord
# nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
def attribute_present?(attribute)
value = read_attribute(attribute)
- !value.nil? || (value.respond_to?(:empty?) && !value.empty?)
+ !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
end
# Returns the column object for the named attribute.
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 8e0b3e1402..51389c84d6 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -111,9 +111,8 @@ module ActiveRecord
# end
# Project.primary_key # => "foo_id"
def primary_key=(value)
- @original_primary_key = @primary_key if defined?(@primary_key)
- @primary_key = value && value.to_s
- @quoted_primary_key = nil
+ @primary_key = value && value.to_s
+ @quoted_primary_key = nil
end
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 4bafadc666..eb3ae7014e 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -191,23 +191,21 @@ module ActiveRecord
# Doesn't use after_save as that would save associations added in after_create/after_update twice
after_create save_method
after_update save_method
+ elsif reflection.macro == :has_one
+ define_method(save_method) { save_has_one_association(reflection) }
+ # Configures two callbacks instead of a single after_save so that
+ # the model may rely on their execution order relative to its
+ # own callbacks.
+ #
+ # For example, given that after_creates run before after_saves, if
+ # we configured instead an after_save there would be no way to fire
+ # a custom after_create callback after the child association gets
+ # created.
+ after_create save_method
+ after_update save_method
else
- if reflection.macro == :has_one
- define_method(save_method) { save_has_one_association(reflection) }
- # Configures two callbacks instead of a single after_save so that
- # the model may rely on their execution order relative to its
- # own callbacks.
- #
- # For example, given that after_creates run before after_saves, if
- # we configured instead an after_save there would be no way to fire
- # a custom after_create callback after the child association gets
- # created.
- after_create save_method
- after_update save_method
- else
- define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
- before_save save_method
- end
+ define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
+ before_save save_method
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index ad2e8634eb..7ee8f40631 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -6,6 +6,9 @@ require 'bigdecimal/util'
module ActiveRecord
module ConnectionAdapters #:nodoc:
+ # Abstract representation of an index definition on a table. Instances of
+ # this type are typically created and returned by methods in database
+ # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where) #:nodoc:
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 6bac05191d..2c210e5ba2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -539,8 +539,8 @@ module ActiveRecord
# ===== Examples
# add_timestamps(:suppliers)
def add_timestamps(table_name)
- add_column table_name, :created_at, :datetime, :null => false
- add_column table_name, :updated_at, :datetime, :null => false
+ add_column table_name, :created_at, :datetime
+ add_column table_name, :updated_at, :datetime
end
# Removes the timestamp columns (created_at and updated_at) from the table definition.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 3cc714040d..767c99de4c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -57,7 +57,7 @@ module ActiveRecord
define_callbacks :checkout, :checkin
attr_accessor :visitor, :pool
- attr_reader :schema_cache, :last_use, :in_use
+ attr_reader :schema_cache, :last_use, :in_use, :logger
alias :in_use? :in_use
def initialize(connection, logger = nil, pool = nil) #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index e33903622b..731c07547a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -375,7 +375,7 @@ module ActiveRecord
def tables(name = nil, database = nil, like = nil) #:nodoc:
sql = "SHOW TABLES "
- sql << "IN #{database} " if database
+ sql << "IN #{quote_table_name(database)} " if database
sql << "LIKE #{quote(like)}" if like
execute_and_free(sql, 'SCHEMA') do |result|
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index d2126a3e19..5b7fa029da 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -978,6 +978,27 @@ module ActiveRecord
end_sql
end
+ # Returns an array of schema names.
+ def schema_names
+ query(<<-SQL).flatten
+ SELECT nspname
+ FROM pg_namespace
+ WHERE nspname !~ '^pg_.*'
+ AND nspname NOT IN ('information_schema')
+ ORDER by nspname;
+ SQL
+ end
+
+ # Creates a schema for the given schema name.
+ def create_schema schema_name
+ execute "CREATE SCHEMA #{schema_name}"
+ end
+
+ # Drops the schema for the given schema name.
+ def drop_schema schema_name
+ execute "DROP SCHEMA #{schema_name} CASCADE"
+ end
+
# Sets the schema search path to a string of comma-separated schema names.
# Names beginning with $ have to be quoted (e.g. $user => '$user').
# See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index c85c4c6e51..3d8dfab05c 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -202,7 +202,7 @@ module ActiveRecord
value = super
if column.type == :string && value.encoding == Encoding::ASCII_8BIT
- @logger.error "Binary data inserted for `string` type on column `#{column.name}`"
+ logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
value.encode! 'utf-8'
end
value
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 3f90d25962..e75a2a1cb4 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -193,7 +193,6 @@ module ActiveRecord
@attributes = self.class.initialize_attributes(coder['attributes'])
@columns_hash = self.class.column_types.merge(coder['column_types'] || {})
-
init_internals
@new_record = false
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 224f5276eb..8d5388e1f3 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -71,7 +71,7 @@ module ActiveRecord
IdentityMap.remove_by_id(symbolized_base_class, id) if IdentityMap.enabled?
- update_all(updates.join(', '), primary_key => id )
+ update_all(updates.join(', '), primary_key => id)
end
# Increment a number field by one, usually representing a count.
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index 60ce3dd4f1..e35b1c91a0 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -1,13 +1,10 @@
module ActiveRecord
module DynamicMatchers
def respond_to?(method_id, include_private = false)
- if match = DynamicFinderMatch.match(method_id)
- return true if all_attributes_exists?(match.attribute_names)
- elsif match = DynamicScopeMatch.match(method_id)
- return true if all_attributes_exists?(match.attribute_names)
- end
+ match = find_dynamic_match(method_id)
+ valid_match = match && all_attributes_exists?(match.attribute_names)
- super
+ valid_match || super
end
private
@@ -22,22 +19,18 @@ module ActiveRecord
# Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
# is first invoked, so that future attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments, &block)
- if match = (DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id))
+ if match = find_dynamic_match(method_id)
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
+
unless match.valid_arguments?(arguments)
method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'"
backtrace = [method_trace] + caller
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace
end
+
if match.respond_to?(:scope?) && match.scope?
- self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
- def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
- attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
- #
- scoped(:conditions => attributes) # scoped(:conditions => attributes)
- end # end
- METHOD
+ define_scope_method(method_id, attribute_names)
send(method_id, *arguments)
elsif match.finder?
options = arguments.extract_options!
@@ -51,17 +44,30 @@ module ActiveRecord
end
end
+ def define_scope_method(method_id, attribute_names) #:nodoc
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
+ conditions = Hash[[:#{attribute_names.join(',:')}].zip(args)] # conditions = Hash[[:user_name, :password].zip(args)]
+ where(conditions) # where(conditions)
+ end # end
+ METHOD
+ end
+
+ def find_dynamic_match(method_id) #:nodoc:
+ DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id)
+ end
+
# Similar in purpose to +expand_hash_conditions_for_aggregates+.
def expand_attribute_names_for_aggregates(attribute_names)
- attribute_names.map { |attribute_name|
- unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
+ attribute_names.map do |attribute_name|
+ if aggregation = reflect_on_aggregation(attribute_name.to_sym)
aggregate_mapping(aggregation).map do |field_attr, _|
field_attr.to_sym
end
else
attribute_name.to_sym
end
- }.flatten
+ end.flatten
end
def all_attributes_exists?(attribute_names)
@@ -73,7 +79,5 @@ module ActiveRecord
mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
mapping.first.is_a?(Array) ? mapping : [mapping]
end
-
-
end
end
diff --git a/activerecord/lib/active_record/dynamic_scope_match.rb b/activerecord/lib/active_record/dynamic_scope_match.rb
index a502155aac..6c043d29c4 100644
--- a/activerecord/lib/active_record/dynamic_scope_match.rb
+++ b/activerecord/lib/active_record/dynamic_scope_match.rb
@@ -7,9 +7,12 @@ module ActiveRecord
# chain more <tt>scoped_by_* </tt> methods after the other. It acts like a named
# scope except that it's dynamic.
class DynamicScopeMatch
+ METHOD_PATTERN = /^scoped_by_([_a-zA-Z]\w*)$/
+
def self.match(method)
- return unless method.to_s =~ /^scoped_by_([_a-zA-Z]\w*)$/
- new(true, $1 && $1.split('_and_'))
+ if method.to_s =~ METHOD_PATTERN
+ new(true, $1 && $1.split('_and_'))
+ end
end
def initialize(scope, attribute_names)
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 4d73cdd37a..9b2dc096a0 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -133,8 +133,7 @@ module ActiveRecord
# Set the column to use for optimistic locking. Defaults to +lock_version+.
def locking_column=(value)
- @original_locking_column = @locking_column if defined?(@locking_column)
- @locking_column = value.to_s
+ @locking_column = value.to_s
end
# The version column used for optimistic locking. Defaults to +lock_version+.
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index b8764217d3..99847ac161 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -114,11 +114,18 @@ module ActiveRecord
# You can also just define your own <tt>self.table_name</tt> method; see
# the documentation for ActiveRecord::Base#table_name.
def table_name=(value)
- @original_table_name = @table_name if defined?(@table_name)
- @table_name = value && value.to_s
- @quoted_table_name = nil
- @arel_table = nil
- @relation = Relation.new(self, arel_table)
+ value = value && value.to_s
+
+ if defined?(@table_name)
+ return if value == @table_name
+ reset_column_information if connected?
+ end
+
+ @table_name = value
+ @quoted_table_name = nil
+ @arel_table = nil
+ @sequence_name = nil unless defined?(@explicit_sequence_name) && @explicit_sequence_name
+ @relation = Relation.new(self, arel_table)
end
# Returns a quoted version of the table name, used to construct SQL statements.
@@ -152,8 +159,7 @@ module ActiveRecord
# Sets the value of inheritance_column
def inheritance_column=(value)
- @original_inheritance_column = inheritance_column
- @inheritance_column = value.to_s
+ @inheritance_column = value.to_s
end
def sequence_name
@@ -165,7 +171,8 @@ module ActiveRecord
end
def reset_sequence_name #:nodoc:
- self.sequence_name = connection.default_sequence_name(table_name, primary_key)
+ @sequence_name = connection.default_sequence_name(table_name, primary_key)
+ @explicit_sequence_name = false
end
# Sets the name of the sequence to use when generating ids to the given
@@ -183,8 +190,8 @@ module ActiveRecord
# self.sequence_name = "projectseq" # default would have been "project_seq"
# end
def sequence_name=(value)
- @original_sequence_name = @sequence_name if defined?(@sequence_name)
@sequence_name = value.to_s
+ @explicit_sequence_name = true
end
# Indicates whether the table associated with this class exists
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 9bc046c775..c4bce87311 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -176,7 +176,7 @@ module ActiveRecord
#
def update_attribute(name, value)
name = name.to_s
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
+ verify_readonly_attribute(name)
send("#{name}=", value)
save(:validate => false)
end
@@ -191,7 +191,7 @@ module ActiveRecord
# attribute is marked as readonly.
def update_column(name, value)
name = name.to_s
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
+ verify_readonly_attribute(name)
raise ActiveRecordError, "can not update on a new record object" unless persisted?
raw_write_attribute(name, value)
self.class.update_all({ name => value }, self.class.primary_key => id) == 1
@@ -323,7 +323,8 @@ module ActiveRecord
changes = {}
attributes.each do |column|
- changes[column.to_s] = write_attribute(column.to_s, current_time)
+ column = column.to_s
+ changes[column] = write_attribute(column, current_time)
end
changes[self.class.locking_column] = increment_lock if locking_enabled?
@@ -369,5 +370,9 @@ module ActiveRecord
@new_record = false
id
end
+
+ def verify_readonly_attribute(name)
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
+ end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 5ab4b93c8b..f26989ae57 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -477,7 +477,7 @@ db_namespace = namespace :db do
db_namespace["test:load_schema"].invoke
when :sql
db_namespace["test:load_structure"].invoke
- end
+ end
end
# desc "Recreate the test database from an existent structure.sql file"
@@ -565,7 +565,7 @@ namespace :railties do
# desc "Copies missing migrations from Railties (e.g. engines). You can specify Railties to use with FROM=railtie1,railtie2"
task :migrations => :'db:load_config' do
to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
- railties = ActiveSupport::OrderedHash.new
+ railties = {}
Rails.application.railties.all do |railtie|
next unless to_load == :all || to_load.include?(railtie.railtie_name)
diff --git a/activerecord/lib/active_record/railties/jdbcmysql_error.rb b/activerecord/lib/active_record/railties/jdbcmysql_error.rb
index 0b75983484..6a38211bff 100644
--- a/activerecord/lib/active_record/railties/jdbcmysql_error.rb
+++ b/activerecord/lib/active_record/railties/jdbcmysql_error.rb
@@ -1,6 +1,6 @@
#FIXME Remove if ArJdbcMysql will give.
module ArJdbcMySQL #:nodoc:
- class Error < StandardError
+ class Error < StandardError #:nodoc:
attr_accessor :error_number, :sql_state
def initialize msg
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 8f8a3ec3bb..d4f4d593c6 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -23,11 +23,11 @@ module ActiveRecord
module ClassMethods
def create_reflection(macro, name, options, active_record)
case macro
- when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
- klass = options[:through] ? ThroughReflection : AssociationReflection
- reflection = klass.new(macro, name, options, active_record)
- when :composed_of
- reflection = AggregateReflection.new(macro, name, options, active_record)
+ when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
+ klass = options[:through] ? ThroughReflection : AssociationReflection
+ reflection = klass.new(macro, name, options, active_record)
+ when :composed_of
+ reflection = AggregateReflection.new(macro, name, options, active_record)
end
self.reflections = self.reflections.merge(name => reflection)
@@ -44,7 +44,8 @@ module ActiveRecord
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
#
def reflect_on_aggregation(aggregation)
- reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
+ reflection = reflections[aggregation]
+ reflection if reflection.is_a?(AggregateReflection)
end
# Returns an array of AssociationReflection objects for all the
@@ -68,7 +69,8 @@ module ActiveRecord
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
#
def reflect_on_association(association)
- reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
+ reflection = reflections[association]
+ reflection if reflection.is_a?(AssociationReflection)
end
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
@@ -77,7 +79,6 @@ module ActiveRecord
end
end
-
# Abstract base class for AggregateReflection and AssociationReflection. Objects of
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
class MacroReflection
@@ -452,7 +453,6 @@ module ActiveRecord
# Recursively fill out the rest of the array from the through reflection
conditions += through_conditions
- # And return
conditions
end
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 510d01085d..c770d36a1c 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -299,7 +299,7 @@ module ActiveRecord
key_records = Hash[key_records.map { |r| [r.id, r] }]
end
- ActiveSupport::OrderedHash[calculated_data.map do |row|
+ Hash[calculated_data.map do |row|
key = group_columns.map { |aliaz, column|
type_cast_calculated_value(row[aliaz], column)
}
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 1d04e763f6..1088773bc7 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -1,7 +1,7 @@
module ActiveRecord
class PredicateBuilder # :nodoc:
def self.build_from_hash(engine, attributes, default_table)
- predicates = attributes.map do |column, value|
+ attributes.map do |column, value|
table = default_table
if value.is_a?(Hash)
@@ -17,20 +17,18 @@ module ActiveRecord
build(table[column.to_sym], value)
end
- end
- predicates.flatten
+ end.flatten
end
def self.references(attributes)
- references = attributes.map do |key, value|
+ attributes.map do |key, value|
if value.is_a?(Hash)
key
else
key = key.to_s
key.split('.').first.to_sym if key.include?('.')
end
- end
- references.compact
+ end.compact
end
private
@@ -43,23 +41,24 @@ module ActiveRecord
values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x}
ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
- array_predicates = ranges.map {|range| attribute.in(range)}
-
- if values.include?(nil)
+ values_predicate = if values.include?(nil)
values = values.compact
+
case values.length
when 0
- array_predicates << attribute.eq(nil)
+ attribute.eq(nil)
when 1
- array_predicates << attribute.eq(values.first).or(attribute.eq(nil))
+ attribute.eq(values.first).or(attribute.eq(nil))
else
- array_predicates << attribute.in(values).or(attribute.eq(nil))
+ attribute.in(values).or(attribute.eq(nil))
end
else
- array_predicates << attribute.in(values)
+ attribute.in(values)
end
- array_predicates.inject {|composite, predicate| composite.or(predicate)}
+ array_predicates = ranges.map { |range| attribute.in(range) }
+ array_predicates << values_predicate
+ array_predicates.inject { |composite, predicate| composite.or(predicate) }
when Range, Arel::Relation
attribute.in(value)
when ActiveRecord::Model
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 2d7d83d160..81b13fe529 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -37,9 +37,9 @@ module ActiveRecord
# { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
def sanitize_sql_for_assignment(assignments)
case assignments
- when Array; sanitize_sql_array(assignments)
- when Hash; sanitize_sql_hash_for_assignment(assignments)
- else assignments
+ when Array; sanitize_sql_array(assignments)
+ when Hash; sanitize_sql_hash_for_assignment(assignments)
+ else assignments
end
end
@@ -57,7 +57,7 @@ module ActiveRecord
def expand_hash_conditions_for_aggregates(attrs)
expanded_attrs = {}
attrs.each do |attr, value|
- unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
+ if aggregation = reflect_on_aggregation(attr.to_sym)
mapping = aggregate_mapping(aggregation)
mapping.each do |field_attr, aggregate_attr|
if mapping.size == 1 && !value.respond_to?(aggregate_attr)
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 4b075183c3..d06020b3ce 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -14,7 +14,7 @@ module ActiveRecord
def initialize(record)
@record = record
errors = @record.errors.full_messages.join(", ")
- super(I18n.t("activerecord.errors.messages.record_invalid", :errors => errors))
+ super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
end
end
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index 99a022461e..f3bb70fb41 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -30,6 +30,10 @@ module ActiveRecord
attributes.select { |a| a.has_index? || (a.reference? && options[:indexes]) }
end
+ def accessible_attributes
+ attributes.reject(&:reference?)
+ end
+
hook_for :test_framework
protected
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/model.rb b/activerecord/lib/rails/generators/active_record/model/templates/model.rb
index 5c47f8b241..d56f9f57a4 100644
--- a/activerecord/lib/rails/generators/active_record/model/templates/model.rb
+++ b/activerecord/lib/rails/generators/active_record/model/templates/model.rb
@@ -3,5 +3,10 @@ class <%= class_name %> < <%= parent_class_name.classify %>
<% attributes.select {|attr| attr.reference? }.each do |attribute| -%>
belongs_to :<%= attribute.name %>
<% end -%>
+<% if !accessible_attributes.empty? -%>
+ attr_accessible <%= accessible_attributes.map {|a| ":#{a.name}" }.sort.join(', ') %>
+<% else -%>
+ # attr_accessible :title, :body
+<% end -%>
end
<% end -%>
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 7fe2c02c04..5d6de90889 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -46,6 +46,16 @@ module ActiveRecord
assert_equal str, value
end
+ def test_tables_quoting
+ begin
+ @conn.tables(nil, "foo-bar", nil)
+ flunk
+ rescue => e
+ # assertion for *quoted* database properly
+ assert_match(/Unknown database 'foo-bar': SHOW TABLES IN `foo-bar`/, e.inspect)
+ end
+ end
+
private
def insert(ctx, data)
binds = data.map { |name, value|
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index d5676bc522..10fdf88592 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -35,6 +35,17 @@ module ActiveRecord
def test_table_exists_wrong_schema
assert(!@connection.table_exists?("#{@db_name}.zomg"), "table should not exist")
end
+
+ def test_tables_quoting
+ begin
+ @connection.tables(nil, "foo-bar", nil)
+ flunk
+ rescue => e
+ # assertion for *quoted* database properly
+ assert_match(/Unknown database 'foo-bar': SHOW TABLES IN `foo-bar`/, e.inspect)
+ end
+ 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 18c81d2b09..9208f53997 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -71,6 +71,45 @@ class SchemaTest < ActiveRecord::TestCase
@connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
end
+ def test_schema_names
+ assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names
+ end
+
+ def test_create_schema
+ begin
+ @connection.create_schema "test_schema3"
+ assert @connection.schema_names.include? "test_schema3"
+ ensure
+ @connection.drop_schema "test_schema3"
+ end
+ end
+
+ def test_raise_create_schema_with_existing_schema
+ begin
+ @connection.create_schema "test_schema3"
+ assert_raises(ActiveRecord::StatementInvalid) do
+ @connection.create_schema "test_schema3"
+ end
+ ensure
+ @connection.drop_schema "test_schema3"
+ end
+ end
+
+ def test_drop_schema
+ begin
+ @connection.create_schema "test_schema3"
+ ensure
+ @connection.drop_schema "test_schema3"
+ end
+ assert !@connection.schema_names.include?("test_schema3")
+ end
+
+ def test_raise_drop_schema_with_nonexisting_schema
+ assert_raises(ActiveRecord::StatementInvalid) do
+ @connection.drop_schema "test_schema3"
+ end
+ end
+
def test_schema_change_with_prepared_stmt
altered = false
@connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]]
@@ -183,13 +222,13 @@ class SchemaTest < ActiveRecord::TestCase
end
def test_raise_on_unquoted_schema_name
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raises(ActiveRecord::StatementInvalid) do
with_schema_search_path '$user,public'
end
end
def test_without_schema_search_path
- assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) }
+ assert_raises(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) }
end
def test_ignore_nil_schema_search_path
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 46da1b0a2b..1dbeb66af6 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -1,6 +1,7 @@
require "cases/helper"
require 'bigdecimal'
require 'yaml'
+require 'securerandom'
module ActiveRecord
module ConnectionAdapters
@@ -12,6 +13,14 @@ module ActiveRecord
:timeout => 100
end
+ def test_type_cast_binary_encoding_without_logger
+ @conn.extend(Module.new { def logger; end })
+ column = Struct.new(:type, :name).new(:string, "foo")
+ binary = SecureRandom.hex
+ expected = binary.dup.encode!('utf-8')
+ assert_equal expected, @conn.type_cast(binary, column)
+ end
+
def test_type_cast_symbol
assert_equal 'foo', @conn.type_cast(:foo, nil)
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 3967009c82..02a7f6af78 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -130,6 +130,28 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal car.id, bulb.car_id
end
+ def test_association_protect_foreign_key
+ invoice = Invoice.create
+
+ line_item = invoice.line_items.new
+ assert_equal invoice.id, line_item.invoice_id
+
+ line_item = invoice.line_items.new :invoice_id => invoice.id + 1
+ assert_equal invoice.id, line_item.invoice_id
+
+ line_item = invoice.line_items.build
+ assert_equal invoice.id, line_item.invoice_id
+
+ line_item = invoice.line_items.build :invoice_id => invoice.id + 1
+ assert_equal invoice.id, line_item.invoice_id
+
+ line_item = invoice.line_items.create
+ assert_equal invoice.id, line_item.invoice_id
+
+ line_item = invoice.line_items.create :invoice_id => invoice.id + 1
+ assert_equal invoice.id, line_item.invoice_id
+ end
+
def test_association_conditions_bypass_attribute_protection
car = Car.create(:name => 'honda')
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 9cc09194dc..12cae934b6 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -688,6 +688,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert author.comments.include?(comment)
end
+ def test_through_association_readonly_should_be_false
+ assert !people(:michael).posts.first.readonly?
+ assert !people(:michael).posts.all.first.readonly?
+ end
+
+ def test_can_update_through_association
+ assert_nothing_raised do
+ people(:michael).posts.first.update_attributes!(:title => "Can write")
+ end
+ end
+
def test_has_many_through_polymorphic_with_primary_key_option
assert_equal [categories(:general)], authors(:david).essay_categories
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 246877bbed..9c05b36426 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -448,6 +448,22 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal car.id, bulb.car_id
end
+ def test_association_protect_foreign_key
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+
+ ship = pirate.build_ship
+ assert_equal pirate.id, ship.pirate_id
+
+ ship = pirate.build_ship :pirate_id => pirate.id + 1
+ assert_equal pirate.id, ship.pirate_id
+
+ ship = pirate.create_ship
+ assert_equal pirate.id, ship.pirate_id
+
+ ship = pirate.create_ship :pirate_id => pirate.id + 1
+ assert_equal pirate.id, ship.pirate_id
+ end
+
def test_association_conditions_bypass_attribute_protection
car = Car.create(:name => 'honda')
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index fb51925a38..ef01476ae4 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -30,9 +30,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase
t = Topic.new
t.title = "hello there!"
t.written_on = Time.now
+ t.author_name = ""
assert t.attribute_present?("title")
assert t.attribute_present?("written_on")
assert !t.attribute_present?("content")
+ assert !t.attribute_present?("author_name")
+
end
def test_attribute_present_with_booleans
@@ -626,9 +629,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
def test_time_zone_aware_attribute_saved
- time_string_1 = "2012-02-20 10:00:00"
- time_string_2 = "2012-02-20 09:00:00"
-
in_time_zone 1 do
record = @target.create(:written_on => '2012-02-20 10:00')
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 698c3d0cb1..01f647b261 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1479,6 +1479,30 @@ class BasicsTest < ActiveRecord::TestCase
end
end
+ def test_clear_cash_when_setting_table_name
+ Joke.table_name = "cold_jokes"
+ before_columns = Joke.columns
+ before_seq = Joke.sequence_name
+
+ Joke.table_name = "funny_jokes"
+ after_columns = Joke.columns
+ after_seq = Joke.sequence_name
+
+ assert_not_equal before_columns, after_columns
+ assert_not_equal before_seq, after_seq unless before_seq.blank? && after_seq.blank?
+ end
+
+ def test_dont_clear_sequence_name_when_setting_explicitly
+ Joke.sequence_name = "black_jokes_seq"
+ Joke.table_name = "cold_jokes"
+ before_seq = Joke.sequence_name
+
+ Joke.table_name = "funny_jokes"
+ after_seq = Joke.sequence_name
+
+ assert_equal before_seq, after_seq unless before_seq.blank? && after_seq.blank?
+ end
+
def test_set_table_name_symbol_converted_to_string
Joke.table_name = :cold_jokes
assert_equal 'cold_jokes', Joke.table_name
@@ -1920,6 +1944,15 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal 1, post.comments.length
end
+ def test_marshalling_new_record_round_trip_with_associations
+ post = Post.new
+ post.comments.build
+
+ post = Marshal.load(Marshal.dump(post))
+
+ assert post.new_record?, "should be a new record"
+ end
+
def test_attribute_names
assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id"],
Company.attribute_names
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index a802cfbf31..917a03bb34 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -89,14 +89,16 @@ class MultipleDbTest < ActiveRecord::TestCase
assert_equal Entrant.arel_engine, Bird.arel_engine
end
- def test_associations_should_work_when_model_has_no_connection
- begin
- ActiveRecord::Model.remove_connection
- assert_nothing_raised ActiveRecord::ConnectionNotEstablished do
- College.first.courses.first
+ unless in_memory_db?
+ def test_associations_should_work_when_model_has_no_connection
+ begin
+ ActiveRecord::Model.remove_connection
+ assert_nothing_raised ActiveRecord::ConnectionNotEstablished do
+ College.first.courses.first
+ end
+ ensure
+ ActiveRecord::Model.establish_connection 'arunit'
end
- ensure
- ActiveRecord::Model.establish_connection 'arunit'
end
end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 2ae9cb4888..09276a034e 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -663,7 +663,7 @@ module NestedAttributesOnACollectionAssociationTests
end
def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
- attributes = ActiveSupport::OrderedHash.new
+ attributes = {}
attributes['123726353'] = { :name => 'Grace OMalley' }
attributes['2'] = { :name => 'Privateers Greed' } # 2 is lower then 123726353
@pirate.send(association_setter, attributes)
@@ -673,7 +673,7 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_raise_an_argument_error_if_something_else_than_a_hash_is_passed
assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, {}) }
- assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, ActiveSupport::OrderedHash.new) }
+ assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, Hash.new) }
assert_raise_with_message ArgumentError, 'Hash or Array expected, got String ("foo")' do
@pirate.send(association_setter, "foo")
diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
index 628029f8df..a8e513d81f 100644
--- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -8,6 +8,16 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
I18n.backend = I18n::Backend::Simple.new
end
+ def reset_i18n_load_path
+ @old_load_path, @old_backend = I18n.load_path.dup, I18n.backend
+ I18n.load_path.clear
+ I18n.backend = I18n::Backend::Simple.new
+ yield
+ ensure
+ I18n.load_path.replace @old_load_path
+ I18n.backend = @old_backend
+ end
+
# validates_associated: generate_message(attr_name, :invalid, :message => custom_message, :value => value)
def test_generate_message_invalid_with_default_message
assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :value => 'title')
@@ -35,4 +45,13 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
assert_equal "Validation failed: Title is invalid, Title can't be blank", ActiveRecord::RecordInvalid.new(topic).message
end
+ test "RecordInvalid exception translation falls back to the :errors namespace" do
+ reset_i18n_load_path do
+ I18n.backend.store_translations 'en', :errors => {:messages => {:record_invalid => 'fallback message'}}
+ topic = Topic.new
+ topic.errors.add(:title, :blank)
+ assert_equal "fallback message", ActiveRecord::RecordInvalid.new(topic).message
+ end
+ end
+
end
diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md
index e092621a33..f305c3fd4a 100644
--- a/activeresource/CHANGELOG.md
+++ b/activeresource/CHANGELOG.md
@@ -16,7 +16,7 @@
*Jim Herz*
-## Rails 3.1.4 (unreleased) ##
+## Rails 3.1.4 (March 1, 2012) ##
* No changes
@@ -44,7 +44,7 @@
end
-## Rails 3.0.12 (unreleased) ##
+## Rails 3.0.12 (March 1, 2012) ##
* No changes.
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 3e40e08ca2..09ec4ed618 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -111,7 +111,7 @@
filehandle, or tune your filesystem.
-## Rails 3.1.4 (unreleased) ##
+## Rails 3.1.4 (March 1, 2012) ##
* No changes
@@ -183,7 +183,7 @@
* JSON decoding now uses the multi_json gem which also vendors a json engine called OkJson. The yaml backend has been removed in favor of OkJson as a default engine for 1.8.x, while the built in 1.9.x json implementation will be used by default. *Josh Kalderimis*
-## Rails 3.0.12 (unreleased) ##
+## Rails 3.0.12 (March 1, 2012) ##
* No changes.
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 dd780da157..4903687b73 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -103,29 +103,39 @@ module ActiveSupport #:nodoc:
end
end
- def[](*args)
- new_safe_buffer = super
- new_safe_buffer.instance_eval { @dirty = false }
- new_safe_buffer
+ def [](*args)
+ return super if args.size < 2
+
+ if html_safe?
+ new_safe_buffer = super
+ new_safe_buffer.instance_eval { @html_safe = true }
+ new_safe_buffer
+ else
+ to_str[*args]
+ end
end
def safe_concat(value)
- raise SafeConcatError if dirty?
+ raise SafeConcatError unless html_safe?
original_concat(value)
end
def initialize(*)
- @dirty = false
+ @html_safe = true
super
end
def initialize_copy(other)
super
- @dirty = other.dirty?
+ @html_safe = other.html_safe?
+ end
+
+ def clone_empty
+ self[0, 0]
end
def concat(value)
- if dirty? || value.html_safe?
+ if !html_safe? || value.html_safe?
super(value)
else
super(ERB::Util.h(value))
@@ -138,7 +148,7 @@ module ActiveSupport #:nodoc:
end
def html_safe?
- !dirty?
+ defined?(@html_safe) && @html_safe
end
def to_s
@@ -161,18 +171,12 @@ module ActiveSupport #:nodoc:
end # end
def #{unsafe_method}!(*args) # def capitalize!(*args)
- @dirty = true # @dirty = true
+ @html_safe = false # @html_safe = false
super # super
end # end
EOT
end
end
-
- protected
-
- def dirty?
- @dirty
- end
end
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 674e4acfd6..e4a13870d7 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -1,10 +1,9 @@
require 'active_support/core_ext/hash/keys'
-# This class has dubious semantics and we only have it so that
-# people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
-# and they get the same value for both keys.
-
module ActiveSupport
+ # This class has dubious semantics and we only have it so that
+ # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
+ # and they get the same value for both keys.
class HashWithIndifferentAccess < Hash
# Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index 82507c1e03..c167efc1a7 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -1,32 +1,32 @@
-# lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of
-# this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead
-# a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used
-# as example but this feature can be applied elsewhere too.
-#
-# Here is an example where +on_load+ method is called to register a hook.
-#
-# initializer "active_record.initialize_timezone" do
-# ActiveSupport.on_load(:active_record) do
-# self.time_zone_aware_attributes = true
-# self.default_timezone = :utc
-# end
-# end
-#
-# When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked.
-# The very last line of +activerecord/lib/active_record/base.rb+ is:
-#
-# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
-#
module ActiveSupport
- @load_hooks = Hash.new {|h,k| h[k] = [] }
- @loaded = {}
+ # lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of
+ # this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead
+ # a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used
+ # as example but this feature can be applied elsewhere too.
+ #
+ # Here is an example where +on_load+ method is called to register a hook.
+ #
+ # initializer "active_record.initialize_timezone" do
+ # ActiveSupport.on_load(:active_record) do
+ # self.time_zone_aware_attributes = true
+ # self.default_timezone = :utc
+ # end
+ # end
+ #
+ # When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked.
+ # The very last line of +activerecord/lib/active_record/base.rb+ is:
+ #
+ # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
+ #
+ @load_hooks = Hash.new { |h,k| h[k] = [] }
+ @loaded = Hash.new { |h,k| h[k] = [] }
def self.on_load(name, options = {}, &block)
- if base = @loaded[name]
+ @loaded[name].each do |base|
execute_hook(base, options, block)
- else
- @load_hooks[name] << [block, options]
end
+
+ @load_hooks[name] << [block, options]
end
def self.execute_hook(base, options, block)
@@ -38,7 +38,7 @@ module ActiveSupport
end
def self.run_load_hooks(name, base = Object)
- @loaded[name] = base
+ @loaded[name] << base
@load_hooks[name].each do |hook, options|
execute_hook(base, options, hook)
end
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 35f400f9df..bfbb838ea7 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -1,28 +1,28 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/try'
-# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following:
-#
-# * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones.
-# * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
-# * Lazily load TZInfo::Timezone instances only when they're needed.
-# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods.
-#
-# If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>:
-#
-# # application.rb:
-# class Application < Rails::Application
-# config.time_zone = "Eastern Time (US & Canada)"
-# end
-#
-# Time.zone # => #<TimeZone:0x514834...>
-# Time.zone.name # => "Eastern Time (US & Canada)"
-# Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
-#
-# The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones
-# defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem
-# (if a recent version of the gem is installed locally, this will be used instead of the bundled version.)
module ActiveSupport
+ # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following:
+ #
+ # * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones.
+ # * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
+ # * Lazily load TZInfo::Timezone instances only when they're needed.
+ # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods.
+ #
+ # If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>:
+ #
+ # # application.rb:
+ # class Application < Rails::Application
+ # config.time_zone = "Eastern Time (US & Canada)"
+ # end
+ #
+ # Time.zone # => #<TimeZone:0x514834...>
+ # Time.zone.name # => "Eastern Time (US & Canada)"
+ # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
+ #
+ # The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones
+ # defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem
+ # (if a recent version of the gem is installed locally, this will be used instead of the bundled version.)
class TimeZone
# Keys are Rails TimeZone names, values are TZInfo identifiers
MAPPING = {
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index dbb6c71907..4551dd2f2d 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -12,7 +12,6 @@ java_import org.xml.sax.InputSource unless defined? InputSource
java_import org.xml.sax.Attributes unless defined? Attributes
java_import org.w3c.dom.Node unless defined? Node
-# = XmlMini JRuby JDOM implementation
module ActiveSupport
module XmlMini_JDOM #:nodoc:
extend self
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 16570c6aea..26556598fd 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -2,7 +2,6 @@ require 'libxml'
require 'active_support/core_ext/object/blank'
require 'stringio'
-# = XmlMini LibXML implementation
module ActiveSupport
module XmlMini_LibXML #:nodoc:
extend self
diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
index 2536b1f33e..acc018fd2d 100644
--- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb
+++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
@@ -2,9 +2,8 @@ require 'libxml'
require 'active_support/core_ext/object/blank'
require 'stringio'
-# = XmlMini LibXML implementation using a SAX-based parser
module ActiveSupport
- module XmlMini_LibXMLSAX
+ module XmlMini_LibXMLSAX #:nodoc:
extend self
# Class that will build the hash while the XML document
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 04ec9e8ab8..bb0a52bdcf 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -7,7 +7,6 @@ end
require 'active_support/core_ext/object/blank'
require 'stringio'
-# = XmlMini Nokogiri implementation
module ActiveSupport
module XmlMini_Nokogiri #:nodoc:
extend self
diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
index 93fd3dfe57..30b94aac47 100644
--- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
@@ -7,9 +7,8 @@ end
require 'active_support/core_ext/object/blank'
require 'stringio'
-# = XmlMini Nokogiri implementation using a SAX-based parser
module ActiveSupport
- module XmlMini_NokogiriSAX
+ module XmlMini_NokogiriSAX #:nodoc:
extend self
# Class that will build the hash while the XML document
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index a13ad10118..a2a87337a6 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -2,7 +2,6 @@ require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/object/blank'
require 'stringio'
-# = XmlMini ReXML implementation
module ActiveSupport
module XmlMini_REXML #:nodoc:
extend self
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index a0f261ebdb..38cdda6c5c 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -550,7 +550,7 @@ class HashExtToParamTests < ActiveSupport::TestCase
end
def test_to_param_orders_by_key_in_ascending_order
- assert_equal 'a=2&b=1&c=0', ActiveSupport::OrderedHash[*%w(b 1 c 0 a 2)].to_param
+ assert_equal 'a=2&b=1&c=0', Hash[*%w(b 1 c 0 a 2)].to_param
end
end
diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb
index 6a26e1fa4f..c34647c1df 100644
--- a/activesupport/test/core_ext/object/to_query_test.rb
+++ b/activesupport/test/core_ext/object/to_query_test.rb
@@ -28,12 +28,12 @@ class ToQueryTest < ActiveSupport::TestCase
def test_nested_conversion
assert_query_equal 'person%5Blogin%5D=seckar&person%5Bname%5D=Nicholas',
- :person => ActiveSupport::OrderedHash[:login, 'seckar', :name, 'Nicholas']
+ :person => Hash[:login, 'seckar', :name, 'Nicholas']
end
def test_multiple_nested
assert_query_equal 'account%5Bperson%5D%5Bid%5D=20&person%5Bid%5D=10',
- ActiveSupport::OrderedHash[:account, {:person => {:id => 20}}, :person, {:id => 10}]
+ Hash[:account, {:person => {:id => 20}}, :person, {:id => 10}]
end
def test_array_values
diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb
index 58ccc14324..7851634dbf 100644
--- a/activesupport/test/lazy_load_hooks_test.rb
+++ b/activesupport/test/lazy_load_hooks_test.rb
@@ -8,6 +8,16 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
assert_equal 1, i
end
+ def test_basic_hook_with_two_registrations
+ i = 0
+ ActiveSupport.on_load(:basic_hook_with_two) { i += incr }
+ assert_equal 0, i
+ ActiveSupport.run_load_hooks(:basic_hook_with_two, FakeContext.new(2))
+ assert_equal 2, i
+ ActiveSupport.run_load_hooks(:basic_hook_with_two, FakeContext.new(5))
+ assert_equal 7, i
+ end
+
def test_hook_registered_after_run
i = 0
ActiveSupport.run_load_hooks(:registered_after)
@@ -16,6 +26,25 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
assert_equal 1, i
end
+ def test_hook_registered_after_run_with_two_registrations
+ i = 0
+ ActiveSupport.run_load_hooks(:registered_after_with_two, FakeContext.new(2))
+ ActiveSupport.run_load_hooks(:registered_after_with_two, FakeContext.new(5))
+ assert_equal 0, i
+ ActiveSupport.on_load(:registered_after_with_two) { i += incr }
+ assert_equal 7, i
+ end
+
+ def test_hook_registered_interleaved_run_with_two_registrations
+ i = 0
+ ActiveSupport.run_load_hooks(:registered_interleaved_with_two, FakeContext.new(2))
+ assert_equal 0, i
+ ActiveSupport.on_load(:registered_interleaved_with_two) { i += incr }
+ assert_equal 2, i
+ ActiveSupport.run_load_hooks(:registered_interleaved_with_two, FakeContext.new(5))
+ assert_equal 7, i
+ end
+
def test_hook_receives_a_context
i = 0
ActiveSupport.on_load(:contextual) { i += incr }
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index bdde5141a9..047b89be2a 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -84,13 +84,13 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal "hello&lt;&gt;", clean + @buffer
end
- test "Should concat as a normal string when dirty" do
+ test "Should concat as a normal string when safe" do
clean = "hello".html_safe
@buffer.gsub!('', '<>')
assert_equal "<>hello", @buffer + clean
end
- test "Should preserve dirty? status on copy" do
+ test "Should preserve html_safe? status on copy" do
@buffer.gsub!('', '<>')
assert !@buffer.dup.html_safe?
end
@@ -102,20 +102,42 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal "<script>", result_buffer
end
- test "Should raise an error when safe_concat is called on dirty buffers" do
+ test "Should raise an error when safe_concat is called on unsafe buffers" do
@buffer.gsub!('', '<>')
assert_raise ActiveSupport::SafeBuffer::SafeConcatError do
@buffer.safe_concat "BUSTED"
end
end
- test "should not fail if the returned object is not a string" do
+ test "Should not fail if the returned object is not a string" do
assert_kind_of NilClass, @buffer.slice("chipchop")
end
- test "Should initialize @dirty to false for new instance when sliced" do
- dirty = @buffer[0,0].send(:dirty?)
- assert_not_nil dirty
- assert !dirty
+ test "clone_empty returns an empty buffer" do
+ assert_equal '', ActiveSupport::SafeBuffer.new('foo').clone_empty
+ end
+
+ test "clone_empty keeps the original dirtyness" do
+ assert @buffer.clone_empty.html_safe?
+ assert !@buffer.gsub!('', '').clone_empty.html_safe?
+ end
+
+ test "Should be safe when sliced if original value was safe" do
+ new_buffer = @buffer[0,0]
+ assert_not_nil new_buffer
+ assert new_buffer.html_safe?, "should be safe"
+ end
+
+ test "Should continue unsafe on slice" do
+ x = 'foo'.html_safe.gsub!('f', '<script>alert("lolpwnd");</script>')
+
+ # calling gsub! makes the dirty flag true
+ assert !x.html_safe?, "should not be safe"
+
+ # getting a slice of it
+ y = x[0..-1]
+
+ # should still be unsafe
+ assert !y.html_safe?, "should not be safe"
end
end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index f556ee210d..960b1ed8ca 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -71,7 +71,7 @@
* Remove old 'config.paths.app.controller' API in favor of 'config.paths["app/controller"]' API *Guillermo Iguaran*
-## Rails 3.1.4 (unreleased) ##
+## Rails 3.1.4 (March 1, 2012) ##
* Setting config.force_ssl also marks the session cookie as secure.
@@ -203,7 +203,7 @@
* Include all helpers from plugins and shared engines in application *Piotr Sarnacki*
-## Rails 3.0.12 (unreleased) ##
+## Rails 3.0.12 (March 1, 2012) ##
* No changes.
diff --git a/railties/guides/code/getting_started/config/database.yml b/railties/guides/code/getting_started/config/database.yml
index 32a998ad72..51a4dd459d 100644
--- a/railties/guides/code/getting_started/config/database.yml
+++ b/railties/guides/code/getting_started/config/database.yml
@@ -6,9 +6,7 @@
development:
adapter: sqlite3
database: db/development.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
# Warning: The database defined as "test" will be erased and
@@ -17,15 +15,11 @@ development:
test:
adapter: sqlite3
database: db/test.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
diff --git a/railties/guides/source/3_2_release_notes.textile b/railties/guides/source/3_2_release_notes.textile
index d669a7fdfa..0f8fea2bf6 100644
--- a/railties/guides/source/3_2_release_notes.textile
+++ b/railties/guides/source/3_2_release_notes.textile
@@ -49,6 +49,18 @@ The <tt>mass_assignment_sanitizer</tt> config also needs to be added in <tt>conf
config.active_record.mass_assignment_sanitizer = :strict
</ruby>
+h4. What to update in your engines
+
+Replace the code beneath the comment in <tt>script/rails</tt> with the following content:
+
+<ruby>
+ENGINE_ROOT = File.expand_path('../..', __FILE__)
+ENGINE_PATH = File.expand_path('../../lib/your_engine_name/engine', __FILE__)
+
+require 'rails/all'
+require 'rails/engine/commands'
+</ruby>
+
h3. Creating a Rails 3.2 application
<shell>
diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile
index 2c0b81121f..f007629207 100644
--- a/railties/guides/source/action_view_overview.textile
+++ b/railties/guides/source/action_view_overview.textile
@@ -535,10 +535,10 @@ h4. AssetTagHelper
This module provides methods for generating HTML that links views to assets such as images, JavaScript files, stylesheets, and feeds.
-By default, Rails links to these assets on the current host in the public folder, but you can direct Rails to link to assets from a dedicated assets server by setting +ActionController::Base.asset_host+ in the application configuration, typically in +config/environments/production.rb+. For example, let's say your asset host is +assets.example.com+:
+By default, Rails links to these assets on the current host in the public folder, but you can direct Rails to link to assets from a dedicated assets server by setting +config.action_controller.asset_host+ in the application configuration, typically in +config/environments/production.rb+. For example, let's say your asset host is +assets.example.com+:
<ruby>
-ActionController::Base.asset_host = "assets.example.com"
+config.action_controller.asset_host = "assets.example.com"
image_tag("rails.png") # => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
</ruby>
diff --git a/railties/guides/source/api_app.textile b/railties/guides/source/api_app.textile
new file mode 100644
index 0000000000..f2d00c5768
--- /dev/null
+++ b/railties/guides/source/api_app.textile
@@ -0,0 +1,276 @@
+h2. Using Rails for API-only Apps
+
+In this guide you will learn:
+
+* What Rails provides for API-only applications
+* How to configure Rails to start without any browser features
+* How to decide which middlewares you will want to include
+* How to decide which modules to use in your controller
+
+NOTE: This guide reflects features that have not yet been fully implemented. Docs first :)
+
+endprologue.
+
+h3. What is an API app?
+
+Traditionally, when people said that they used Rails as an "API", they meant
+providing a programmatically accessible API alongside their web application.
+For example, GitHub provides "an API":http://developer.github.com that you can use from your own custom clients.
+
+With the advent of client-side frameworks, more developers are using Rails to build a backend that is shared between their web application and other native applications.
+
+For example, Twitter uses its "public API":https://dev.twitter.com in its web application, which is built as a static site that consumes JSON resources.
+
+Instead of using Rails to generate dynamic HTML that will communicate with the server through forms and links, many developers are treating their web application as just another client, delivered as static HTML, CSS and JavaScript, and consuming a simple JSON API
+
+This guide covers building a Rails application that serves JSON resources to an API client *or* client-side framework.
+
+h3. Why use Rails for JSON APIs?
+
+The first question a lot of people have when thinking about building a JSON API using Rails is: "isn't using Rails to spit out some JSON overkill? Shouldn't I just use something like Sinatra?"
+
+For very simple APIs, this may be true. However, even in very HTML-heavy applications, most of an application's logic is actually outside of the view layer.
+
+The reason most people use Rails is that it provides a set of defaults that allows us to get up and running quickly without having to make a lot of trivial decisions.
+
+Let's take a look at some of the things that Rails provides out of the box that are still applicable to API applications.
+
+Handled at the middleware layer:
+
+* Reloading: Rails applications support transparent reloading. This works even if your application gets big and restarting the server for every request becomes non-viable.
+* Development Mode: Rails application come with smart defaults for development, making development pleasant without compromising production-time performance.
+* Test Mode: Ditto test mode.
+* Logging: Rails applications log every request, with a level of verbosity appropriate for the current mode. Rails logs in development include information about the request environment, database queries, and basic performance information.
+* Security: Rails detects and thwarts "IP spoofing attacks":http://en.wikipedia.org/wiki/IP_address_spoofing and handles cryptographic signatures in a "timing attack":http://en.wikipedia.org/wiki/Timing_attack aware way. Don't know what an IP spoofing attack or a timing attack is? Exactly.
+* Parameter Parsing: Want to specify your parameters as JSON instead of as a URL-encoded String? No problem. Rails will decode the JSON for you and make it available in +params+. Want to use nested URL-encoded params? That works too.
+* Conditional GETs: Rails handles conditional +GET+, (+ETag+ and +Last-Modified+), processing request headers and returning the correct response headers and status code. All you need to do is use the "stale?":http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F check in your controller, and Rails will handle all of the HTTP details for you.
+* Caching: If you use +dirty?+ with public cache control, Rails will automatically cache your responses. You can easily configure the cache store.
+* HEAD requests: Rails will transparently convert +HEAD+ requests into +GET+ requests, and return just the headers on the way out. This makes +HEAD+ work reliably in all Rails APIs.
+
+While you could obviously build these up in terms of existing Rack middlewares, I think this list demonstrates that the default Rails middleware stack provides a lot of value, even if you're "just generating JSON".
+
+Handled at the ActionPack layer:
+
+* Resourceful Routing: If you're building a RESTful JSON API, you want to be using the Rails router. Clean and conventional mapping from HTTP to controllers means not having to spend time thinking about how to model your API in terms of HTTP.
+* URL Generation: The flip side of routing is URL generation. A good API based on HTTP includes URLs (see "the GitHub gist API":http://developer.github.com/v3/gists/ for an example).
+* Header and Redirection Responses: +head :no_content+ and +redirect_to user_url(current_user)+ come in handy. Sure, you could manually add the response headers, but why?
+* Content Negotiation: The Rails +respond_to+ and +respond_with+ features automatically figure out which MIME type to serve, based on the request's +Accept+ header and available types. If you ever need to add support for types other than JSON (XML, CSV, or some proprietary format), this will come in handy.
+* Caching: Rails provides page, action and fragment caching. Fragment caching is especially helpful when building up a nested JSON object.
+* Basic, Digest and Token Authentication: Rails comes with out-of-the-box support for three kinds of HTTP authentication.
+* Instrumentation: Rails 3.0 added an instrumentation API that will trigger registered handlers for a variety of events, such as action processing, sending a file or data, redirection, and database queries. The payload of each event comes with relevant information (for the action processing event, the payload includes the controller, action, params, request format, request method and the request's full path).
+* Generators: This may be passé for advanced Rails users, but it can be nice to generate a resource and get your model, controller, test stubs, and routes created for you in a single command.
+* Plugins: Many third-party libraries come with support for Rails that reduces or eliminates the cost of setting up and gluing together the library and the web framework. This includes things like overriding default generators, adding rake tasks, and honoring Rails choices (like the logger and cache backend).
+
+Of course, the Rails boot process also glues together all registered components. For example, the Rails boot process is what uses your +config/database.yml+ file when configuring ActiveRecord.
+
+**The short version is**: you may not have thought about which parts of Rails are still applicable even if you remove the view layer, but the answer turns out to be "most of it".
+
+h3. The Basic Configuration
+
+If you're building a Rails application that will be an API server first and foremost, you can start with a more limited subset of Rails and add in features as needed.
+
+You can generate a new bare Rails app:
+
+<shell>
+$ rails new my_api --api
+</shell>
+
+This will do three main things for you:
+
+* Configure your application to start with a more limited set of middleware than normal. Specifically, it will not include any middleware primarily useful for browser applications (like cookie support) by default.
+* Make +ApplicationController+ inherit from +ActionController::API+ instead of +ActionController::Base+. As with middleware, this will leave out any +ActionController+ modules that provide functionality primarily used by browser applications.
+* Configure the generators to skip generating views, helpers and assets when you generate a new resource.
+
+If you want to take an existing app and make it an API app, follow the following steps.
+
+In +config/application.rb+ add the following lines at the top of the +Application+ class:
+
+<ruby>
+config.middleware.api_only!
+config.generators.api_only!
+</ruby>
+
+Change +app/controllers/application_controller.rb+:
+
+<ruby>
+# instead of
+class ApplicationController < ActionController::Base
+end
+
+# do
+class ApplicationController < ActionController::API
+end
+</ruby>
+
+h3. Choosing Middlewares
+
+An API application comes with the following middlewares by default.
+
+* +Rack::Cache+: Caches responses with public +Cache-Control+ headers using HTTP caching semantics. See below for more information.
+* +Rack::Sendfile+: Uses a front-end server's file serving support from your Rails application.
+* +Rack::Lock+: If your application is not marked as threadsafe (+config.threadsafe!+), this middleware will add a mutex around your requests.
+* +ActionDispatch::RequestId+:
+* +Rails::Rack::Logger+:
+* +ActionDispatch::ShowExceptions+: Rescue exceptions and re-dispatch them to an exception handling application
+* +ActionDispatch::DebugExceptions+: Log exceptions
+* +ActionDispatch::RemoteIp+: Protect against IP spoofing attacks
+* +ActionDispatch::Reloader+: In development mode, support code reloading.
+* +ActionDispatch::ParamsParser+: Parse XML, YAML and JSON parameters when the request's +Content-Type+ is one of those.
+* +ActionDispatch::Head+: Dispatch +HEAD+ requests as +GET+ requests, and return only the status code and headers.
+* +Rack::ConditionalGet+: Supports the +stale?+ feature in Rails controllers.
+* +Rack::ETag+: Automatically set an +ETag+ on all string responses. This means that if the same response is returned from a controller for the same URL, the server will return a +304 Not Modified+, even if no additional caching steps are taken. This is primarily a client-side optimization; it reduces bandwidth costs but not server processing time.
+
+Other plugins, including +ActiveRecord+, may add additional middlewares. In general, these middlewares are agnostic to the type of app you are building, and make sense in an API-only Rails application.
+
+You can get a list of all middlewares in your application via:
+
+<shell>
+$ rake middleware
+</shell>
+
+h4. Using Rack::Cache
+
+When used with Rails, +Rack::Cache+ uses the Rails cache store for its entity and meta stores. This means that if you use memcache, for your Rails app, for instance, the built-in HTTP cache will use memcache.
+
+To make use of +Rack::Cache+, you will want to use +stale?+ in your controller. Here's an example of +stale?+ in use.
+
+<ruby>
+def show
+ @post = Post.find(params[:id])
+
+ if stale?(:last_modified => @post.updated_at)
+ render json: @post
+ end
+end
+</ruby>
+
+The call to +stale?+ will compare the +If-Modified-Since+ header in the request with +@post.updated_at+. If the header is newer than the last modified, this action will return a +304 Not Modified+ response. Otherwise, it will render the response and include a +Last-Modified+ header with the response.
+
+Normally, this mechanism is used on a per-client basis. +Rack::Cache+ allows us to share this caching mechanism across clients. We can enable cross-client caching in the call to +stale?+
+
+<ruby>
+def show
+ @post = Post.find(params[:id])
+
+ if stale?(:last_modified => @post.updated_at, :public => true)
+ render json: @post
+ end
+end
+</ruby>
+
+This means that +Rack::Cache+ will store off +Last-Modified+ value for a URL in the Rails cache, and add an +If-Modified-Since+ header to any subsequent inbound requests for the same URL.
+
+Think of it as page caching using HTTP semantics.
+
+NOTE: The +Rack::Cache+ middleware is always outside of the +Rack::Lock+ mutex, even in single-threaded apps.
+
+h4. Using Rack::Sendfile
+
+When you use the +send_file+ method in a Rails controller, it sets the +X-Sendfile+ header. +Rack::Sendfile+ is responsible for actually sending the file.
+
+If your front-end server supports accelerated file sending, +Rack::Sendfile+ will offload the actual file sending work to the front-end server.
+
+You can configure the name of the header that your front-end server uses for this purposes using +config.action_dispatch.x_sendfile_header+ in the appropriate environment config file.
+
+You can learn more about how to use +Rack::Sendfile+ with popular front-ends in "the Rack::Sendfile documentation":http://rubydoc.info/github/rack/rack/master/Rack/Sendfile
+
+The values for popular servers once they are configured to support accelerated file sending:
+
+<ruby>
+# Apache and lighttpd
+config.action_dispatch.x_sendfile_header = "X-Sendfile"
+
+# nginx
+config.action_dispatch.x_sendfile_header = "X-Accel-Redirect"
+</ruby>
+
+Make sure to configure your server to support these options following the instructions in the +Rack::Sendfile+ documentation.
+
+NOTE: The +Rack::Sendfile+ middleware is always outside of the +Rack::Lock+ mutex, even in single-threaded apps.
+
+h4. Using ActionDispatch::ParamsParser
+
++ActionDispatch::ParamsParser+ will take parameters from the client in JSON and make them available in your controller as +params+.
+
+To use this, your client will need to make a request with JSON-encoded parameters and specify the +Content-Type+ as +application/json+.
+
+Here's an example in jQuery:
+
+<plain>
+jQuery.ajax({
+ type: 'POST',
+ url: '/people'
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
+
+ success: function(json) { }
+});
+</plain>
+
++ActionDispatch::ParamsParser+ will see the +Content-Type+ and your params will be +{ :person => { :firstName => "Yehuda", :lastName => "Katz" } }+.
+
+h4. Other Middlewares
+
+Rails ships with a number of other middlewares that you might want to use in an API app, especially if one of your API clients is the browser:
+
+* +Rack::SSL+: Redirects any HTTP request to HTTPS.
+* +Rack::Runtime+: Adds a header to the response listing the total runtime of the request.
+* +Rack::MethodOverride+: Allows the use of the +_method+ hack to route POST requests to other verbs.
+* +ActionDispatch::Cookies+: Supports the +cookie+ method in +ActionController+, including support for signed and encrypted cookies.
+* +ActionDispatch::Flash+: Supports the +flash+ mechanism in +ActionController+.
+* +ActionDispatch::BestStandards+: Tells Internet Explorer to use the most standards-compliant available renderer. In production mode, if ChromeFrame is available, use ChromeFrame.
+* Session Management: If a +config.session_store+ is supplied, this middleware makes the session available as the +session+ method in +ActionController+.
+
+Any of these middlewares can be adding via:
+
+<ruby>
+config.middleware.use Rack::MethodOverride
+</ruby>
+
+h4. Removing Middlewares
+
+If you don't want to use a middleware that is included by default in the API-only middleware set, you can remove it using +config.middleware.delete+:
+
+<ruby>
+config.middleware.delete ::Rack::Sendfile
+</ruby>
+
+Keep in mind that removing these features may remove support for certain features in +ActionController+.
+
+h3. Choosing Controller Modules
+
+An API application (using +ActionController::API+) comes with the following controller modules by default:
+
+* +AbstractController::Translation+: Support for the +l+ and +t+ localization and translation methods. These delegate to +I18n.translate+ and +I18n.localize+.
+* +ActionController::UrlFor+: Makes +url_for+ and friends available.
+* +ActionController::Redirecting+: Support for +redirect_to+
+* +ActionController::Renderers::JSON+: Support for +render :json+
+* +ActionController::ConditionalGet+: Support for +stale?+
+* +ActionController::RackDelegation+: Support for the +request+ and +response+ methods returning +ActionDispatch::Request+ and +ActionDispatch::Response+ objects.
+* +ActionController::MimeResponds+: Support for content negotiation (+respond_to+, +respond_with+)
+* +ActionController::DataStreaming+: Support for +send_file+ and +send_data+
+* +AbstractController::Callbacks+: Support for +before_filter+ and friends
+* +ActionController::Instrumentation+: Support for the instrumentation hooks defined by +ActionController+ (see "the source":https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/instrumentation.rb for more).
+
+Other plugins may add additional modules. You can get a list of all modules included into +ActionController::API+ in the rails console:
+
+<shell>
+$ irb
+>> ActionController::API.ancestors - ActionController::Metal.ancestors
+</shell>
+
+h4. Adding Other Modules
+
+All ActionController modules know about their dependent modules, so you can feel free to include any modules into your controllers, and all dependencies will be included and set up as well.
+
+Some common modules you might want to add:
+
+* +ActionController::HTTPAuthentication::Basic+ (or +Digest+ or +Token): Support for basic, digest or token HTTP authentication.
+* +ActionController::Rendering+: Support for templating and +ActionView+.
+* +AbstractController::Layouts+: Support for layouts when rendering.
+* +ActionController::Renderers::XML+: Support for +render :xml+.
+* +ActionController::Cookies+: Support for +cookies+, which includes support for signed and encrypted cookies. This requires the cookie middleware.
+* +ActionController::Rescue+: Support for +rescue_from+.
+
+The best place to add a module is in your +ApplicationController+. You can also add modules to individual controllers.
diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile
index ff2bd08602..a061c1fc16 100644
--- a/railties/guides/source/asset_pipeline.textile
+++ b/railties/guides/source/asset_pipeline.textile
@@ -423,12 +423,14 @@ It is important that this folder is shared between deployments so that remotely
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 non-JS/CSS files (i.e., +.coffee+ and +.scss+ files are *not* automatically included as they compile to JS/CSS):
+The default matcher for compiling files includes +application.js+, +application.css+ and all non-JS/CSS files (this will include all image assets automatically):
<ruby>
[ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, /application.(css|js)$/ ]
</ruby>
+NOTE. The matcher (and other members of the precompile array; see below) is applied to final compiled file names. This means that anything that compiles to JS/CSS is excluded, as well as raw JS/CSS files; for example, +.coffee+ and +.scss+ files are *not* automatically included as they compile to JS/CSS.
+
If you have other manifests or individual stylesheets and JavaScript files to include, you can add them to the +precompile+ array:
<erb>
@@ -456,7 +458,7 @@ config.assets.manifest = '/path/to/some/other/location'
NOTE: If there are missing precompiled files in production you will get an <tt>Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError</tt> exception indicating the name of the missing file(s).
-h5. Server Configuration
+h5. Far-future Expires header
Precompiled assets exist on the filesystem and are served directly by your web server. 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.
@@ -464,6 +466,7 @@ For Apache:
<plain>
<LocationMatch "^/assets/.*$">
+ # Use of ETag is discouraged when Last-Modified is present
Header unset ETag
FileETag None
# RFC says only cache for 1 year
@@ -484,6 +487,8 @@ location ~ ^/assets/ {
}
</plain>
+h5. GZip compression
+
When files are precompiled, Sprockets also creates a "gzipped":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. Web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, Sprockets uses the maximum compression ratio, thus reducing the size of the data transfer to the minimum. On the other hand, web servers can be configured to serve compressed content directly from disk, rather than deflating non-compressed files themselves.
Nginx is able to do this automatically enabling +gzip_static+:
diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index 8ae8c61ae6..fe4a84dae9 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -521,9 +521,7 @@ development:
adapter: postgresql
encoding: unicode
database: gitapp_development
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: gitapp
password:
...
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index e796f44606..2f465b37ed 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -652,19 +652,17 @@ The error occurred while evaluating nil.each
h3. Database pooling
-Active Record database connections are managed by +ActiveRecord::ConnectionAdapters::ConnectionPool+ which ensures that a connection pool synchronizes the amount of thread access to a limited number of database connections. This limit defaults to 1 and can be configured in +database.yml+.
+Active Record database connections are managed by +ActiveRecord::ConnectionAdapters::ConnectionPool+ which ensures that a connection pool synchronizes the amount of thread access to a limited number of database connections. This limit defaults to 5 and can be configured in +database.yml+.
<ruby>
development:
adapter: sqlite3
database: db/development.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
</ruby>
-Since the connection pooling is handled inside of Active Record by default, all application servers (Thin, Mongrel, Unicorn etc.) should behave the same. Initially, the database connection pool is empty and it will create additional connections as the demand for them increases, until it reaches the connection pool limit.
+Since the connection pooling is handled inside of ActiveRecord by default, all application servers (Thin, mongrel, Unicorn etc.) should behave the same. Initially, the database connection pool is empty and it will create additional connections as the demand for them increases, until it reaches the connection pool limit.
Any one request will check out a connection the first time it requires access to the database, after which it will check the connection back in, at the end of the request, meaning that the additional connection slot will be available again for the next request in the queue.
diff --git a/railties/guides/source/engines.textile b/railties/guides/source/engines.textile
index e6058e7581..5f7eb5290c 100644
--- a/railties/guides/source/engines.textile
+++ b/railties/guides/source/engines.textile
@@ -77,7 +77,7 @@ end
By inheriting from the +Rails::Engine+ class, this gem notifies Rails that there's an engine at the specified path, and will correctly mount the engine inside the application, performing tasks such as adding the +app+ directory of the engine to the load path for models, mailers, controllers and views.
-The +isolate_namespace+ method here deserves special notice. This call is responsible for isolating the controllers, models, routes and other things into their own namespace, away from similar components inside hte application. Without this, there is a possibility that the engine's components could "leak" into the application, causing unwanted disruption, or that important engine components could be overriden by similarly named things within the application.
+The +isolate_namespace+ method here deserves special notice. This call is responsible for isolating the controllers, models, routes and other things into their own namespace, away from similar components inside hte application. Without this, there is a possibility that the engine's components could "leak" into the application, causing unwanted disruption, or that important engine components could be overriden by similarly named things within the application. One of the examples of such conflicts are helpers. Without calling +isolate_namespace+, engine's helpers would be included in application's controllers.
NOTE: It is *highly* recommended that the +isolate_namespace+ line be left within the +Engine+ class definition. Without it, classes generated in an engine *may* conflict with an application.
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index d6f3c3e217..bed14ef6a8 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -329,9 +329,7 @@ environment:
development:
adapter: sqlite3
database: db/development.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
</yaml>
@@ -352,9 +350,7 @@ development:
adapter: mysql2
encoding: utf8
database: blog_development
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: root
password:
socket: /tmp/mysql.sock
@@ -374,9 +370,7 @@ development:
adapter: postgresql
encoding: unicode
database: blog_development
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: blog
password:
</yaml>
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index 25201888e7..320f1e9d20 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -521,7 +521,7 @@ h5. Bulk and Namespace Lookup
To look up multiple translations at once, an array of keys can be passed:
<ruby>
-I18n.t [:odd, :even], :scope => 'activerecord.errors.messages'
+I18n.t [:odd, :even], :scope => 'errors.messages'
# => ["must be odd", "must be even"]
</ruby>
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 42665114be..c5567b3350 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -622,6 +622,7 @@ You can specify what Rails should route +"/"+ to with the +root+ method:
<ruby>
root :to => 'pages#main'
+root 'pages#main' # shortcut for the above
</ruby>
You should put the +root+ route at the top of the file, because it is the most popular route and should be matched first. You also need to delete the +public/index.html+ file for the root route to take effect.
diff --git a/railties/guides/source/upgrading_ruby_on_rails.textile b/railties/guides/source/upgrading_ruby_on_rails.textile
index 3588a67196..6e84b7fe40 100644
--- a/railties/guides/source/upgrading_ruby_on_rails.textile
+++ b/railties/guides/source/upgrading_ruby_on_rails.textile
@@ -4,15 +4,39 @@ This guide provides steps to be followed when you upgrade your applications to a
endprologue.
-h3. Rails Upgrades
+h3. General Advice
-When you're upgrading an existing application, it's always a great idea to have good test coverage before going in. Rails 3 and above requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.2.x will be the last branch to support 1.8.7 and Rails 4 (current edge) will support only Ruby 1.9.3.
+Before attempting to upgrade an existing application, you should be sure you have a good reason to upgrade. You need to balance out several factors: the need for new features, the increasing difficulty of finding support for old code, and your available time and skills, to name a few.
-TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump on to 1.9.2 or 1.9.3 for smooth sailing.
+h4(#general_testing). Test Coverage
+
+The best way to be sure that your application still works after upgrading is to have good test coverage before you start the process. If you don't have automated tests that exercise the bulk of your application, you'll need to spend time manually exercising all the parts that have changed. In the case of a Rails upgrade, that will mean every single piece of functionality in the application. Do yourself a favor and make sure your test coverage is good _before_ you start an upgrade.
+
+h4(#general_ruby). Ruby Versions
+
+Rails generally stays close to the latest released Ruby version when it's released:
+
+* Rails 3 and above requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible.
+* Rails 3.2.x will be the last branch to support Ruby 1.8.7.
+* Rails 4 will support only Ruby 1.9.3.
+
+TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump on to 1.9.2 or 1.9.3 for smooth sailing.
+
+h3. Upgrading from Rails 3.2 to Rails 4.0
+
+NOTE: This section is a work in progress.
+
+If your application is currently on any version of Rails older than 3.2.x, you should upgrade to Rails 3.2 before attempting an update to Rails 4.0.
+
+The following changes are meant for upgrading your application to Rails 4.0.
+
+h4(#plugins4_0). vendor/plugins
+
+Rails 4.0 no longer supports loading plugins from <tt>vendor/plugins</tt>. You must replace any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, <tt>lib/my_plugin/*</tt> and add an appropriate initializer in <tt>config/initializers/my_plugin.rb</tt>.
h3. Upgrading from Rails 3.1 to Rails 3.2
-We recommend that you first upgrade to Rails 3.1 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 3.2.
+If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2.
The following changes are meant for upgrading your application to Rails 3.2.1, the latest 3.2.x version of Rails.
@@ -32,7 +56,7 @@ end
h4(#config_dev3_2). config/environments/development.rb
-* There are a couple of new configuration changes you'd want to add:
+There are a couple of new configuration settings that you should add to your development environment:
<ruby>
# Raise exception on mass assignment protection for Active Record models
@@ -45,7 +69,7 @@ config.active_record.auto_explain_threshold_in_seconds = 0.5
h4(#config_test3_2). config/environments/test.rb
-The <tt>mass_assignment_sanitizer</tt> config also needs to be added in <tt>config/environments/test.rb</tt>:
+The <tt>mass_assignment_sanitizer</tt> configuration setting should also be be added to <tt>config/environments/test.rb</tt>:
<ruby>
# Raise exception on mass assignment protection for Active Record models
@@ -54,11 +78,11 @@ config.active_record.mass_assignment_sanitizer = :strict
h4(#plugins3_2). vendor/plugins
-* Rails 3.2 deprecates <tt>vendor/plugins</tt> and Rails 4.0 will remove them completely. You can start replacing these plugins by extracting them as gems and adding them in your Gemfile. If you choose not to make them gems, you can move them into, say, <tt>lib/my_plugin/*</tt> and add an appropriate initializer in <tt>config/initializers/my_plugin.rb</tt>.
+Rails 3.2 deprecates <tt>vendor/plugins</tt> and Rails 4.0 will remove them completely. While it's not strictly necessary as part of a Rails 3.2 upgrade, you can start replacing any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, <tt>lib/my_plugin/*</tt> and add an appropriate initializer in <tt>config/initializers/my_plugin.rb</tt>.
h3. Upgrading from Rails 3.0 to Rails 3.1
-We recommend that you first upgrade to Rails 3.0 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 3.1.
+If your application is currently on any version of Rails older than 3.0.x, you should upgrade to Rails 3.0 before attempting an update to Rails 3.1.
The following changes are meant for upgrading your application to Rails 3.1.3, the latest 3.1.x version of Rails.
@@ -83,14 +107,14 @@ gem 'jquery-rails'
h4(#config_app3_1). config/application.rb
-* The asset pipeline requires the following additions:
+The asset pipeline requires the following additions:
<ruby>
config.assets.enabled = true
config.assets.version = '1.0'
</ruby>
-* If your application is using the "/assets" route for a resource you may want change the prefix used for assets to avoid conflicts:
+If your application is using an "/assets" route for a resource you may want change the prefix used for assets to avoid conflicts:
<ruby>
# Defaults to '/assets'
@@ -99,9 +123,9 @@ config.assets.prefix = '/asset-files'
h4(#config_dev3_1). config/environments/development.rb
-* Remove the RJS setting <tt>config.action_view.debug_rjs = true</tt>.
+Remove the RJS setting <tt>config.action_view.debug_rjs = true</tt>.
-* Add the following, if you enable the asset pipeline.
+Add these settings if you enable the asset pipeline:
<ruby>
# Do not compress assets
@@ -113,7 +137,7 @@ config.assets.debug = true
h4(#config_prod3_1). config/environments/production.rb
-* Again, most of the changes below are for the asset pipeline. You can read more about these in the "Asset Pipeline":asset_pipeline.html guide.
+Again, most of the changes below are for the asset pipeline. You can read more about these in the "Asset Pipeline":asset_pipeline.html guide.
<ruby>
# Compress JavaScripts and CSS
@@ -137,6 +161,8 @@ config.assets.digest = true
h4(#config_test3_1). config/environments/test.rb
+You can help test performance with these additions to your test environment:
+
<ruby>
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_assets = true
@@ -145,7 +171,7 @@ config.static_cache_control = "public, max-age=3600"
h4(#config_wp3_1). config/initializers/wrap_parameters.rb
-* Add this file with the following contents, if you wish to wrap parameters into a nested hash. This is on by default in new applications.
+Add this file with the following contents, if you wish to wrap parameters into a nested hash. This is on by default in new applications.
<ruby>
# Be sure to restart your server when you modify this file.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index c163081bfc..10fa63c303 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -229,32 +229,32 @@ module Rails
middleware.use ::Rack::SSL, config.ssl_options
end
+ if config.action_dispatch.x_sendfile_header.present?
+ middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
+ end
+
if config.serve_static_assets
middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
end
middleware.use ::Rack::Lock unless config.allow_concurrency
middleware.use ::Rack::Runtime
- middleware.use ::Rack::MethodOverride
+ middleware.use ::Rack::MethodOverride unless config.middleware.api_only?
middleware.use ::ActionDispatch::RequestId
middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods
middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
middleware.use ::ActionDispatch::DebugExceptions
middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
- if config.action_dispatch.x_sendfile_header.present?
- middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
- end
-
unless config.cache_classes
app = self
middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? }
end
middleware.use ::ActionDispatch::Callbacks
- middleware.use ::ActionDispatch::Cookies
+ middleware.use ::ActionDispatch::Cookies unless config.middleware.api_only?
- if config.session_store
+ if !config.middleware.api_only? && config.session_store
if config.force_ssl && !config.session_options.key?(:secure)
config.session_options[:secure] = true
end
@@ -267,7 +267,7 @@ module Rails
middleware.use ::Rack::ConditionalGet
middleware.use ::Rack::ETag, "no-cache"
- if config.action_dispatch.best_standards_support
+ if !config.middleware.api_only? && config.action_dispatch.best_standards_support
middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support
end
end
diff --git a/railties/lib/rails/application/route_inspector.rb b/railties/lib/rails/application/route_inspector.rb
index 2ca0c68243..1e5ce67a58 100644
--- a/railties/lib/rails/application/route_inspector.rb
+++ b/railties/lib/rails/application/route_inspector.rb
@@ -64,7 +64,7 @@ module Rails
# executes `rake routes`. People should not use this class.
class RouteInspector # :nodoc:
def initialize
- @engines = ActiveSupport::OrderedHash.new
+ @engines = Hash.new
end
def format all_routes, filter = nil
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index f8ad17773a..0efa21d82c 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -9,6 +9,14 @@ module Rails
class MiddlewareStackProxy #:nodoc:
def initialize
@operations = []
+ @api_only = false
+ end
+
+ attr_reader :api_only
+ alias :api_only? :api_only
+
+ def api_only!
+ @api_only = true
end
def insert_before(*args, &block)
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 acf47a03e5..e47784994a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -54,7 +54,7 @@ module <%= app_const_base %>
# This will create an empty whitelist of attributes available for mass-assignment for all models
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
- # config.active_record.whitelist_attributes = true
+ <%= comment_if :skip_active_record %>config.active_record.whitelist_attributes = true
# Specifies wether or not has_many or has_one association option :dependent => :restrict raises
# an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
index 950016ad92..c3349912aa 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
@@ -12,9 +12,7 @@ development:
adapter: mysql2
encoding: utf8
database: <%= app_name %>_development
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: root
password:
<% if mysql_socket -%>
@@ -30,9 +28,7 @@ test:
adapter: mysql2
encoding: utf8
database: <%= app_name %>_test
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: root
password:
<% if mysql_socket -%>
@@ -45,9 +41,7 @@ production:
adapter: mysql2
encoding: utf8
database: <%= app_name %>_production
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: root
password:
<% if mysql_socket -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
index a8ed15c2dc..f08f86aac3 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
@@ -16,9 +16,7 @@ development:
adapter: postgresql
encoding: unicode
database: <%= app_name %>_development
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: <%= app_name %>
password:
@@ -44,9 +42,7 @@ test:
adapter: postgresql
encoding: unicode
database: <%= app_name %>_test
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: <%= app_name %>
password:
@@ -54,8 +50,6 @@ production:
adapter: postgresql
encoding: unicode
database: <%= app_name %>_production
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
username: <%= app_name %>
password:
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
index 32a998ad72..51a4dd459d 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
@@ -6,9 +6,7 @@
development:
adapter: sqlite3
database: db/development.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
# Warning: The database defined as "test" will be erased and
@@ -17,15 +15,11 @@ development:
test:
adapter: sqlite3
database: db/test.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
- # Maximum number of database connections available per process. Please
- # increase this number in multithreaded applications.
- pool: 1
+ pool: 5
timeout: 5000
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index 5ad51f8476..0354c08067 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -20,6 +20,7 @@ class LoadingTest < ActiveSupport::TestCase
app_file "app/models/post.rb", <<-MODEL
class Post < ActiveRecord::Base
validates_acceptance_of :title, :accept => "omg"
+ attr_accessible :title
end
MODEL
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
index 0591386a87..eb791f5687 100644
--- a/railties/test/application/middleware/sendfile_test.rb
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -57,5 +57,18 @@ module ApplicationTests
get "/"
assert_equal File.expand_path(__FILE__), last_response.headers["X-Lighttpd-Send-File"]
end
+
+ test "files handled by ActionDispatch::Static are handled by Rack::Sendfile" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = 'X-Sendfile'
+ app.config.serve_static_assets = true
+ app.paths["public"] = File.join(rails_root, "public")
+ end
+
+ app_file "public/foo.txt", "foo"
+
+ get "/foo.txt", "HTTP_X_SENDFILE_TYPE" => "X-Sendfile"
+ assert_equal File.join(rails_root, "public/foo.txt"), last_response.headers["X-Sendfile"]
+ end
end
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index d0a550d2f0..a190a31fc7 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -26,6 +26,7 @@ module ApplicationTests
boot!
assert_equal [
+ "Rack::Sendfile",
"ActionDispatch::Static",
"Rack::Lock",
"ActiveSupport::Cache::Strategy::LocalCache",
@@ -36,7 +37,6 @@ module ApplicationTests
"ActionDispatch::ShowExceptions",
"ActionDispatch::DebugExceptions",
"ActionDispatch::RemoteIp",
- "Rack::Sendfile",
"ActionDispatch::Reloader",
"ActionDispatch::Callbacks",
"ActiveRecord::ConnectionAdapters::ConnectionManagement",
@@ -52,6 +52,36 @@ module ApplicationTests
], middleware
end
+ test "api middleware stack" do
+ add_to_config "config.middleware.api_only!"
+ add_to_config "config.force_ssl = true"
+ add_to_config "config.action_dispatch.x_sendfile_header = 'X-Sendfile'"
+
+ boot!
+
+ assert_equal [
+ "Rack::SSL",
+ "Rack::Sendfile",
+ "ActionDispatch::Static",
+ "Rack::Lock",
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ "Rack::Runtime",
+ "ActionDispatch::RequestId",
+ "Rails::Rack::Logger",
+ "ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
+ "ActionDispatch::RemoteIp",
+ "ActionDispatch::Reloader",
+ "ActionDispatch::Callbacks",
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement",
+ "ActiveRecord::QueryCache",
+ "ActionDispatch::ParamsParser",
+ "ActionDispatch::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag"
+ ], middleware
+ end
+
test "Rack::Sendfile is not included by default" do
boot!
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index a3c24c392b..cf6f9b90c9 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -202,6 +202,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator [destination_root, "--skip-active-record"]
assert_no_file "config/database.yml"
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
+ assert_file "config/application.rb", /#\s+config\.active_record\.whitelist_attributes = true/
assert_file "config/application.rb", /#\s+config\.active_record\.dependent_restrict_raises = false/
assert_file "test/test_helper.rb" do |helper_content|
assert_no_match(/fixtures :all/, helper_content)
@@ -350,6 +351,11 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_active_record_whitelist_attributes_is_present_application_config
+ run_generator
+ assert_file "config/application.rb", /config\.active_record\.whitelist_attributes = true/
+ end
+
def test_active_record_dependent_restrict_raises_is_present_application_config
run_generator
assert_file "config/application.rb", /config\.active_record\.dependent_restrict_raises = false/
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 156fa86eee..e8d933935d 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -317,4 +317,14 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
end
+
+ def test_attr_accessible_added_with_non_reference_attributes
+ run_generator
+ assert_file 'app/models/account.rb', /attr_accessible :age, :name/
+ end
+
+ def test_attr_accessible_added_with_comments_when_no_attributes_present
+ run_generator ["Account"]
+ assert_file 'app/models/account.rb', /# attr_accessible :title, :body/
+ end
end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 4fb5e6a4eb..b0be555c4c 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -247,7 +247,10 @@ module TestHelpers
:activemodel,
:activerecord,
:activeresource] - arr
- remove_from_config "config.active_record.dependent_restrict_raises = false" if to_remove.include? :activerecord
+ if to_remove.include? :activerecord
+ remove_from_config "config.active_record.whitelist_attributes = true"
+ remove_from_config "config.active_record.dependent_restrict_raises = false"
+ end
$:.reject! {|path| path =~ %r'/(#{to_remove.join('|')})/' }
end