aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile12
-rw-r--r--README.md35
-rw-r--r--actionmailer/README.rdoc2
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb2
-rw-r--r--actionmailer/test/delivery_methods_test.rb3
-rw-r--r--actionpack/CHANGELOG.md7
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb49
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb3
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb2
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb4
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb3
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb56
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb5
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb24
-rw-r--r--actionpack/test/controller/new_base/render_streaming_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_text_test.rb15
-rw-r--r--actionpack/test/dispatch/response_test.rb5
-rw-r--r--actionpack/test/fixtures/helpers/abc_helper.rb2
-rw-r--r--actionview/CHANGELOG.md4
-rw-r--r--actionview/actionview.gemspec2
-rw-r--r--actionview/lib/action_view/digestor.rb10
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb2
-rw-r--r--actionview/lib/action_view/rendering.rb41
-rw-r--r--actionview/test/fixtures/helpers/abc_helper.rb2
-rw-r--r--actionview/test/template/digestor_test.rb13
-rw-r--r--actionview/test/template/form_collections_helper_test.rb4
-rw-r--r--actionview/test/template/resolver_patterns_test.rb2
-rw-r--r--actionview/test/template/url_helper_test.rb1
-rw-r--r--activemodel/lib/active_model/secure_password.rb4
-rw-r--r--activemodel/test/cases/serializers/json_serialization_test.rb2
-rw-r--r--activemodel/test/cases/validations/conditional_validation_test.rb14
-rw-r--r--activerecord/CHANGELOG.md22
-rw-r--r--activerecord/RUNNING_UNIT_TESTS.rdoc1
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb16
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_part.rb15
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb4
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb42
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb7
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb17
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb5
-rw-r--r--activerecord/lib/active_record/fixtures.rb119
-rw-r--r--activerecord/lib/active_record/model_schema.rb44
-rw-r--r--activerecord/lib/active_record/reflection.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb3
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb51
-rw-r--r--activerecord/lib/active_record/relation/merger.rb12
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb29
-rw-r--r--activerecord/lib/active_record/result.rb16
-rw-r--r--activerecord/lib/active_record/scoping/default.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb5
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb10
-rw-r--r--activerecord/test/cases/associations/eager_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb18
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb98
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/base_test.rb29
-rw-r--r--activerecord/test/cases/dup_test.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb1
-rw-r--r--activerecord/test/cases/fixtures_test.rb41
-rw-r--r--activerecord/test/cases/relation/where_test.rb25
-rw-r--r--activerecord/test/cases/relation_test.rb40
-rw-r--r--activerecord/test/cases/relations_test.rb18
-rw-r--r--activerecord/test/fixtures/tasks.yml2
-rw-r--r--activerecord/test/models/author.rb3
-rw-r--r--activerecord/test/models/auto_id.rb4
-rw-r--r--activerecord/test/models/car.rb3
-rw-r--r--activerecord/test/models/citation.rb3
-rw-r--r--activerecord/test/models/club.rb1
-rw-r--r--activerecord/test/models/company.rb2
-rw-r--r--activerecord/test/models/member.rb1
-rw-r--r--activerecord/test/models/movie.rb4
-rw-r--r--activerecord/test/models/post.rb9
-rw-r--r--activerecord/test/models/project.rb1
-rw-r--r--activerecord/test/models/topic.rb1
-rw-r--r--activesupport/CHANGELOG.md5
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb1
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb2
-rw-r--r--activesupport/lib/active_support/dependencies.rb2
-rw-r--r--activesupport/lib/active_support/duration.rb10
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/html/some_class.rb4
-rw-r--r--activesupport/test/core_ext/bigdecimal_test.rb1
-rw-r--r--activesupport/test/core_ext/duration_test.rb3
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb7
-rw-r--r--activesupport/test/dependencies_test.rb8
-rw-r--r--activesupport/test/transliterate_test.rb6
-rw-r--r--activesupport/test/xml_mini_test.rb12
-rw-r--r--guides/bug_report_templates/active_record_gem.rb2
-rw-r--r--guides/code/getting_started/test/fixtures/comments.yml2
-rw-r--r--guides/code/getting_started/test/fixtures/posts.yml2
-rw-r--r--guides/source/4_0_release_notes.md6
-rw-r--r--guides/source/_welcome.html.erb2
-rw-r--r--guides/source/action_controller_overview.md44
-rw-r--r--guides/source/action_mailer_basics.md28
-rw-r--r--guides/source/active_record_callbacks.md20
-rw-r--r--guides/source/active_record_querying.md35
-rw-r--r--guides/source/active_support_core_extensions.md10
-rw-r--r--guides/source/asset_pipeline.md4
-rw-r--r--guides/source/caching_with_rails.md2
-rw-r--r--guides/source/command_line.md2
-rw-r--r--guides/source/debugging_rails_applications.md2
-rw-r--r--guides/source/development_dependencies_install.md15
-rw-r--r--guides/source/engines.md7
-rw-r--r--guides/source/form_helpers.md6
-rw-r--r--guides/source/getting_started.md70
-rw-r--r--guides/source/i18n.md11
-rw-r--r--guides/source/initialization.md12
-rw-r--r--guides/source/migrations.md9
-rw-r--r--guides/source/routing.md4
-rw-r--r--guides/source/security.md2
-rw-r--r--guides/source/testing.md37
-rw-r--r--guides/source/upgrading_ruby_on_rails.md6
-rw-r--r--railties/lib/rails/api/task.rb12
-rw-r--r--railties/lib/rails/app_rails_loader.rb2
-rw-r--r--railties/lib/rails/engine.rb9
-rw-r--r--railties/lib/rails/engine/railties.rb2
-rw-r--r--railties/lib/rails/generators/base.rb31
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb6
-rw-r--r--railties/lib/rails/railtie/configuration.rb2
-rw-r--r--railties/test/application/basic_rendering_test.rb62
-rw-r--r--railties/test/application/configuration_test.rb7
-rw-r--r--railties/test/generators/namespaced_generators_test.rb2
140 files changed, 1077 insertions, 596 deletions
diff --git a/Gemfile b/Gemfile
index d703a2e404..7ac743cbd6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -61,12 +61,12 @@ platforms :ruby do
end
platforms :jruby do
- git 'git://github.com/jruby/activerecord-jdbc-adapter.git' do
- gem 'activerecord-jdbcsqlite3-adapter'
- group :db do
- gem 'activerecord-jdbcmysql-adapter'
- gem 'activerecord-jdbcpostgresql-adapter'
- end
+ gem 'json'
+ gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.0'
+
+ group :db do
+ gem 'activerecord-jdbcmysql-adapter', '>= 1.3.0'
+ gem 'activerecord-jdbcpostgresql-adapter', '>= 1.3.0'
end
end
diff --git a/README.md b/README.md
index 31ee4c1086..95a66aea28 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,6 @@ pattern.
Understanding the MVC pattern is key to understanding Rails. MVC divides your
application into three layers, each with a specific responsibility.
-The _View layer_ is composed of "templates" that are responsible for providing
-appropriate representations of your application's resources. Templates can
-come in a variety of formats, but most view templates are HTML with embedded
-Ruby code (ERB files).
-
The _Model layer_ represents your domain model (such as Account, Product,
Person, Post, etc.) and encapsulates the business logic that is specific to
your application. In Rails, database-backed model classes are derived from
@@ -24,16 +19,26 @@ as provided by the Active Model module. You can read more about Active Record
in its [README](activerecord/README.rdoc).
The _Controller layer_ is responsible for handling incoming HTTP requests and
-providing a suitable response. Usually this means returning HTML, but Rails
-controllers can also generate XML, JSON, PDFs, mobile-specific views, and
-more. Controllers manipulate models and render view templates in order to
-generate the appropriate HTTP response.
-
-In Rails, the Controller and View layers are handled together by Action Pack.
-These two layers are bundled in a single package due to their heavy interdependence.
-This is unlike the relationship between Active Record and Action Pack, which are
-independent. Each of these packages can be used independently outside of Rails. You
-can read more about Action Pack in its [README](actionpack/README.rdoc).
+providing a suitable response. Usually this means returning HTML, but Rails controllers
+can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers load and
+manipulate models, and render view templates in order to generate the appropriate HTTP response.
+In Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and
+controller classes are derived from `ActionController::Base`. Action Dispatch and Action Controller
+are bundled together in Action Pack. You can read more about Action Pack in its
+[README](actionpack/README.rdoc).
+
+The _View layer_ is composed of "templates" that are responsible for providing
+appropriate representations of your application's resources. Templates can
+come in a variety of formats, but most view templates are HTML with embedded
+Ruby code (ERB files). Views are typically rendered to generate a controller response,
+or to generate the body of an email. In Rails, View generation is handled by Action View.
+You can read more about Action View in its [README](actionview/README.rdoc).
+
+Active Record, Action Pack, and Action View can each be used independently outside Rails.
+In addition to them, Rails also comes with Action Mailer ([README](actionmailer/README.rdoc)), a library
+to generate and send emails; and Active Support ([README](activesupport/README.rdoc)), a collection of
+utility classes and standard library extensions that are useful for Rails, and may also be used
+independently outside Rails.
## Getting Started
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index a14a6ba18f..178572706c 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -61,7 +61,7 @@ generated would look like this:
Thank you for signing up!
-In previous version of Rails you would call <tt>create_method_name</tt> and
+In previous versions of Rails you would call <tt>create_method_name</tt> and
<tt>deliver_method_name</tt>. Rails 3.0 has a much simpler interface - you
simply call the method and optionally call +deliver+ on the return value.
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index 9a1a27c8ed..aedcd81e52 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -64,7 +64,7 @@ module ActionMailer
raise "Delivery method cannot be nil"
when Symbol
if klass = delivery_methods[method]
- mail.delivery_method(klass,(send(:"#{method}_settings") || {}).merge!(options || {}))
+ mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {}))
else
raise "Invalid delivery method #{method.inspect}"
end
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 61a037ea18..20412c7bb2 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -152,6 +152,9 @@ class MailDeliveryTest < ActiveSupport::TestCase
assert_equal "overridden", delivery_method_instance.settings[:user_name]
assert_equal "somethingobtuse", delivery_method_instance.settings[:password]
assert_equal delivery_method_instance.settings.merge(overridden_options), delivery_method_instance.settings
+
+ # make sure that overriding delivery method options per mail instance doesn't affect the Base setting
+ assert_equal settings, ActionMailer::Base.smtp_settings
end
test "non registered delivery methods raises errors" do
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index e7621270dd..b11154b352 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,9 +1,4 @@
-* Introduce `BasicRendering` which is the most basic rendering implementation. It
- allows to `render :text` and `render :nothing` without need of having ActionView.
-
- *Łukasz Strzałkowski*
-
-* Separate ActionView completely from ActionPack.
+* Separate Action View completely from Action Pack.
*Łukasz Strzałkowski*
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 5a5c47eb3b..6f6079d3c5 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -18,8 +18,14 @@ module AbstractController
self.protected_instance_variables = []
end
- def default_protected_instance_vars
- [:@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config]
+ # Normalize arguments, options and then delegates render_to_body and
+ # sticks the result in self.response_body.
+ # :api: public
+ def render(*args, &block)
+ options = _normalize_render(*args, &block)
+ self.response_body = render_to_body(options)
+ _process_format(rendered_format)
+ self.response_body
end
# Raw rendering of a template to a string.
@@ -34,24 +40,26 @@ module AbstractController
# overridden in order to still return a string.
# :api: plugin
def render_to_string(*args, &block)
+ options = _normalize_render(*args, &block)
+ render_to_body(options)
end
- # Raw rendering of a template.
- # :api: plugin
- def render_to_body(options = {})
- end
-
- # Normalize arguments, options and then delegates render_to_body and
- # sticks the result in self.response_body.
+ # Performs the actual template rendering.
# :api: public
- def render(*args, &block)
+ def render_to_body(options = {})
end
# Return Content-Type of rendered content
# :api: public
def rendered_format
+ Mime::TEXT
end
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w(
+ @_action_name @_response_body @_formats @_prefixes @_config
+ @_view_context_class @_view_renderer @_lookup_context
+ )
+
# This method should return a hash with assigns.
# You can overwrite this configuration per controller.
# :api: public
@@ -59,7 +67,7 @@ module AbstractController
hash = {}
variables = instance_variables
variables -= protected_instance_variables
- variables -= default_protected_instance_vars
+ variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) }
hash
end
@@ -68,7 +76,11 @@ module AbstractController
# render "foo/bar" to render :file => "foo/bar".
# :api: plugin
def _normalize_args(action=nil, options={})
- options
+ if action.is_a? Hash
+ action
+ else
+ options
+ end
end
# Normalize options.
@@ -82,5 +94,18 @@ module AbstractController
def _process_options(options)
options
end
+
+ # Process the rendered format.
+ # :api: private
+ def _process_format(format)
+ end
+
+ # Normalize args and options.
+ # :api: private
+ def _normalize_render(*args, &block)
+ options = _normalize_args(*args, &block)
+ _normalize_options(options)
+ options
+ end
end
end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index d6b1908ccb..417d2efec2 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -13,7 +13,6 @@ module ActionController
autoload :Middleware
autoload_under "metal" do
- autoload :BasicRendering, 'action_controller/metal/rendering'
autoload :Compatibility
autoload :ConditionalGet
autoload :Cookies
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 9941c06201..4b2c00c86a 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -3,7 +3,7 @@ require "action_controller/metal/params_wrapper"
module ActionController
# The <tt>metal</tt> anonymous class was introduced to solve issue with including modules in <tt>ActionController::Base</tt>.
- # Modules needes to be included in particluar order. First wee need to have <tt>AbstractController::Rendering</tt> included,
+ # Modules needes to be included in particluar order. First we need to have <tt>AbstractController::Rendering</tt> included,
# next we should include actuall implementation which would be for example <tt>ActionView::Rendering</tt> and after that
# <tt>ActionController::Rendering</tt>. This order must be preserved and as we want to have middle module included dynamicaly
# <tt>metal</tt> class was introduced. It has <tt>AbstractController::Rendering</tt> included and is parent class of
@@ -14,7 +14,6 @@ module ActionController
#
metal = Class.new(Metal) do
include AbstractController::Rendering
- include ActionController::BasicRendering
end
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index b8afce42c9..a2cb6d1e66 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -48,7 +48,7 @@ module ActionController
# You can pass any of the following options to affect the redirect status and response
# * <tt>status</tt> - Redirect with a custom status (default is 301 Moved Permanently)
# * <tt>flash</tt> - Set a flash message when redirecting
- # * <tt>alert</tt> - Set a alert message when redirecting
+ # * <tt>alert</tt> - Set an alert message when redirecting
# * <tt>notice</tt> - Set a notice message when redirecting
#
# ==== Action Options
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 834d44f045..66dabd821f 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -364,9 +364,7 @@ module ActionController #:nodoc:
format = collector.negotiate_format(request)
if format
- self.content_type ||= format.to_s
- lookup_context.formats = [format.to_sym]
- lookup_context.rendered_format = lookup_context.formats.first
+ _process_format(format)
collector
else
raise ActionController::UnknownFormat
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index abed6e53cc..62a3844b04 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -8,8 +8,7 @@ module ActionController
class MissingRenderer < LoadError
def initialize(format)
- @format = format
- super("No renderer defined for format: #{@format}")
+ super "No renderer defined for format: #{format}"
end
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 21224b9c3b..90f0ef0b1c 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -1,36 +1,4 @@
module ActionController
- # Basic rendering implements the most minimal rendering layer.
- # It only supports rendering :text and :nothing. Passing any other option will
- # result in `UnsupportedOperationError` exception. For more functionality like
- # different formats, layouts etc. you should use `ActionView` gem.
- module BasicRendering
- extend ActiveSupport::Concern
-
- # Render text or nothing (empty string) to response_body
- # :api: public
- def render(*args, &block)
- super(*args, &block)
- opts = args.first
- if opts.has_key?(:text) && opts[:text].present?
- self.response_body = opts[:text]
- elsif opts.has_key?(:nothing) && opts[:nothing]
- self.response_body = " "
- else
- raise UnsupportedOperationError
- end
- end
-
- def rendered_format
- Mime::TEXT
- end
-
- class UnsupportedOperationError < StandardError
- def initialize
- super "Unsupported render operation. BasicRendering supports only :text and :nothing options. For more, you need to include ActionView."
- end
- end
- end
-
module Rendering
extend ActiveSupport::Concern
@@ -44,27 +12,35 @@ module ActionController
def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if self.response_body
super
- self.content_type ||= rendered_format.to_s
- self.response_body
end
# Overwrite render_to_string because body can now be set to a rack body.
def render_to_string(*)
- if self.response_body = super
+ result = super
+ if result.respond_to?(:each)
string = ""
- self.response_body.each { |r| string << r }
+ result.each { |r| string << r }
string
+ else
+ result
end
- ensure
- self.response_body = nil
end
- def render_to_body(*)
- super || " "
+ def render_to_body(options = {})
+ super || if options[:text].present?
+ options[:text]
+ else
+ " "
+ end
end
private
+ def _process_format(format)
+ super
+ self.content_type ||= format.to_s
+ end
+
# Normalize arguments by catching blocks and setting them on :update.
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
options = super
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index b279ef81a9..ae600b1ebe 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -298,7 +298,7 @@ module ActionController
# params.slice(:d) # => {}
def slice(*keys)
self.class.new(super).tap do |new_instance|
- new_instance.instance_variable_set :@permitted, @permitted
+ new_instance.permitted = @permitted
end
end
@@ -312,10 +312,15 @@ module ActionController
# copy_params.permitted? # => true
def dup
super.tap do |duplicate|
- duplicate.instance_variable_set :@permitted, @permitted
+ duplicate.permitted = @permitted
end
end
+ protected
+ def permitted=(new_permitted)
+ @permitted = new_permitted
+ end
+
private
def convert_hashes_to_parameters(key, value)
if value.is_a?(Parameters) || !value.is_a?(Hash)
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index d3696cbb8a..5247e61a23 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -41,7 +41,7 @@ module ActionDispatch # :nodoc:
# Get and set headers for this response.
attr_accessor :header
-
+
alias_method :headers=, :header=
alias_method :headers, :header
@@ -181,9 +181,9 @@ module ActionDispatch # :nodoc:
end
alias_method :status_message, :message
- def respond_to?(method)
+ def respond_to?(method, include_private = false)
if method.to_s == 'to_path'
- stream.respond_to?(:to_path)
+ stream.respond_to?(method)
else
super
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 288ce3e867..600c49e442 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -362,8 +362,9 @@ module ActionDispatch
# # Yes, controller actions are just rack endpoints
# match 'photos/:id', to: PhotosController.action(:show)
#
- # Because request various HTTP verbs with a single action has security
- # implications, is recommendable use HttpHelpers[rdoc-ref:HttpHelpers]
+ # Because requesting various HTTP verbs with a single action has security
+ # implications, you must either specify the actions in
+ # the via options or use one of the HtttpHelpers[rdoc-ref:HttpHelpers]
# instead +match+
#
# === Options
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 0e5dc1fc6c..943fc15026 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -514,11 +514,12 @@ module ActionDispatch
@recall = recall.dup
@set = set
+ normalize_recall!
normalize_options!
normalize_controller_action_id!
use_relative_controller!
normalize_controller!
- handle_nil_action!
+ normalize_action!
end
def controller
@@ -537,6 +538,11 @@ module ActionDispatch
end
end
+ # Set 'index' as default action for recall
+ def normalize_recall!
+ @recall[:action] ||= 'index'
+ end
+
def normalize_options!
# If an explicit :controller was given, always make :action explicit
# too, so that action expiry works as expected for things like
@@ -552,8 +558,8 @@ module ActionDispatch
options[:controller] = options[:controller].to_s
end
- if options[:action]
- options[:action] = options[:action].to_s
+ if options.key?(:action)
+ options[:action] = (options[:action] || 'index').to_s
end
end
@@ -563,8 +569,6 @@ module ActionDispatch
# :controller, :action or :id is not found, don't pull any
# more keys from the recall.
def normalize_controller_action_id!
- @recall[:action] ||= 'index' if current_controller
-
use_recall_for(:controller) or return
use_recall_for(:action) or return
use_recall_for(:id)
@@ -586,13 +590,11 @@ module ActionDispatch
@options[:controller] = controller.sub(%r{^/}, '') if controller
end
- # This handles the case of action: nil being explicitly passed.
- # It is identical to action: "index"
- def handle_nil_action!
- if options.has_key?(:action) && options[:action].nil?
- options[:action] = 'index'
+ # Move 'index' action from options to recall
+ def normalize_action!
+ if @options[:action] == 'index'
+ @recall[:action] = @options.delete(:action)
end
- recall[:action] = options.delete(:action) if options[:action] == 'index'
end
# Generates a path from routes, returns [path, params].
diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb
index f4bdd3e1d4..2b36a399bb 100644
--- a/actionpack/test/controller/new_base/render_streaming_test.rb
+++ b/actionpack/test/controller/new_base/render_streaming_test.rb
@@ -92,7 +92,7 @@ module RenderStreaming
io.rewind
assert_match "(undefined method `invalid!' for nil:NilClass)", io.read
ensure
- ActionController::Base.logger = _old
+ ActionView::Base.logger = _old
end
end
diff --git a/actionpack/test/controller/new_base/render_text_test.rb b/actionpack/test/controller/new_base/render_text_test.rb
index d6c3926a4d..2a253799f3 100644
--- a/actionpack/test/controller/new_base/render_text_test.rb
+++ b/actionpack/test/controller/new_base/render_text_test.rb
@@ -1,6 +1,15 @@
require 'abstract_unit'
module RenderText
+ class MinimalController < ActionController::Metal
+ include AbstractController::Rendering
+ include ActionController::Rendering
+
+ def index
+ render text: "Hello World!"
+ end
+ end
+
class SimpleController < ActionController::Base
self.view_paths = [ActionView::FixtureResolver.new]
@@ -63,6 +72,12 @@ module RenderText
end
class RenderTextTest < Rack::TestCase
+ test "rendering text from a minimal controller" do
+ get "/render_text/minimal/index"
+ assert_body "Hello World!"
+ assert_status 200
+ end
+
test "rendering text from an action with default options renders the text with the layout" do
with_routing do |set|
set.draw { get ':controller', :action => 'index' }
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 2fbe7358f9..4501ea095c 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -212,6 +212,11 @@ class ResponseTest < ActiveSupport::TestCase
ActionDispatch::Response.default_headers = nil
end
end
+
+ test "respond_to? accepts include_private" do
+ assert_not @response.respond_to?(:method_missing)
+ assert @response.respond_to?(:method_missing, true)
+ end
end
class ResponseIntegrationTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/fixtures/helpers/abc_helper.rb b/actionpack/test/fixtures/helpers/abc_helper.rb
index 7104ff3730..cf2774bb5f 100644
--- a/actionpack/test/fixtures/helpers/abc_helper.rb
+++ b/actionpack/test/fixtures/helpers/abc_helper.rb
@@ -1,5 +1,3 @@
module AbcHelper
def bare_a() end
- def bare_b() end
- def bare_c() end
end
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 98b78a7114..6240dc6ac6 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Only cache template digests if `config.cache_template_loading` id true.
+
+ *Josh Lauer*, *Justin Ridgewell*
+
* Added an `extname` hash option for `javascript_include_tag` method.
Before:
diff --git a/actionview/actionview.gemspec b/actionview/actionview.gemspec
index 87cb568300..cdac074973 100644
--- a/actionview/actionview.gemspec
+++ b/actionview/actionview.gemspec
@@ -5,7 +5,7 @@ Gem::Specification.new do |s|
s.name = 'actionview'
s.version = version
s.summary = 'Rendering framework putting the V in MVC (part of Rails).'
- s.description = ''
+ s.description = 'Simple, battle-tested conventions and helpers for building web pages.'
s.required_ruby_version = '>= 1.9.3'
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 64239c81b2..95c3200c81 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -32,9 +32,13 @@ module ActionView
Digestor
end
- @@cache[cache_key] = digest = klass.new(name, format, finder, options).digest # Store the actual digest
- ensure
- @@cache.delete_pair(cache_key, false) if pre_stored && !digest # something went wrong, make sure not to corrupt the @@cache
+ # Store the actual digest if config.cache_template_loading is true
+ klass.new(name, format, finder, options).digest.tap do |digest|
+ @@cache[cache_key] = digest if ActionView::Resolver.caching?
+ end
+ rescue Exception
+ @@cache.delete_pair(cache_key, false) if pre_stored # something went wrong, make sure not to corrupt the @@cache
+ raise
end
end
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index 0b957adb91..c830ab23e3 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -143,9 +143,9 @@ module ActionView
"#{source}#{tail}"
end
- alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
+ alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with an asset_path named route
- # Computes the full URL to a asset in the public directory. This
+ # Computes the full URL to an asset in the public directory. This
# will use +asset_path+ internally, so most of their behaviors
# will be the same.
def asset_url(source, options = {})
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 2a38e5c446..b3af1d4da4 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -4,7 +4,7 @@ module ActionView
module CacheHelper
# This helper exposes a method for caching fragments of a view
# rather than an entire action or page. This technique is useful
- # caching pieces like menus, lists of newstopics, static HTML
+ # caching pieces like menus, lists of new topics, static HTML
# fragments, and so on. This method takes a block that contains
# the content you wish to cache.
#
diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb
index db9c4ef501..18a0788f8e 100644
--- a/actionview/lib/action_view/rendering.rb
+++ b/actionview/lib/action_view/rendering.rb
@@ -77,27 +77,6 @@ module ActionView
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
end
- # Render template to response_body
- # :api: public
- def render(*args, &block)
- options = _normalize_render(*args, &block)
- self.response_body = render_to_body(options)
- end
-
- # Raw rendering of a template to a string.
- # :api: public
- def render_to_string(*args, &block)
- options = _normalize_render(*args, &block)
- render_to_body(options)
- end
-
- # Raw rendering of a template.
- # :api: public
- def render_to_body(options = {})
- _process_options(options)
- _render_template(options)
- end
-
# Find and renders a template based on the options given.
# :api: private
def _render_template(options) #:nodoc:
@@ -105,22 +84,22 @@ module ActionView
view_renderer.render(view_context, options)
end
- def rendered_format
- Mime[lookup_context.rendered_format]
+ def render_to_body(options = {})
+ _process_options(options)
+ _render_template(options)
end
- def default_protected_instance_vars
- super.concat([:@_view_context_class, :@_view_renderer, :@_lookup_context])
+ def rendered_format
+ Mime[lookup_context.rendered_format]
end
private
- # Normalize args and options.
- # :api: private
- def _normalize_render(*args, &block)
- options = _normalize_args(*args, &block)
- _normalize_options(options)
- options
+ # Assign the rendered format to lookup context.
+ def _process_format(format) #:nodoc:
+ super
+ lookup_context.formats = [format.to_sym]
+ lookup_context.rendered_format = lookup_context.formats.first
end
# Normalize args by converting render "foo" to render :action => "foo" and
diff --git a/actionview/test/fixtures/helpers/abc_helper.rb b/actionview/test/fixtures/helpers/abc_helper.rb
index 7104ff3730..cf2774bb5f 100644
--- a/actionview/test/fixtures/helpers/abc_helper.rb
+++ b/actionview/test/fixtures/helpers/abc_helper.rb
@@ -1,5 +1,3 @@
module AbcHelper
def bare_a() end
- def bare_b() end
- def bare_c() end
end
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index 67e3775f28..c6608e214a 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -184,6 +184,15 @@ class TemplateDigestorTest < ActionView::TestCase
assert_not_equal digest_phone, digest_fridge_phone
end
+ def test_cache_template_loading
+ resolver_before = ActionView::Resolver.caching
+ ActionView::Resolver.caching = false
+ assert_digest_difference("messages/edit", true) do
+ change_template("comments/_comment")
+ end
+ ActionView::Resolver.caching = resolver_before
+ end
+
private
def assert_logged(message)
old_logger = ActionView::Base.logger
@@ -200,9 +209,9 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
- def assert_digest_difference(template_name)
+ def assert_digest_difference(template_name, persistent = false)
previous_digest = digest(template_name)
- ActionView::Digestor.cache.clear
+ ActionView::Digestor.cache.clear unless persistent
yield
diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb
index bc9c21dfd3..5b8afddc41 100644
--- a/actionview/test/template/form_collections_helper_test.rb
+++ b/actionview/test/template/form_collections_helper_test.rb
@@ -38,7 +38,7 @@ class FormCollectionsHelperTest < ActionView::TestCase
assert_select 'label[for=user_active_no]', 'No'
end
- test 'colection radio should sanitize collection values for labels correctly' do
+ test 'collection radio should sanitize collection values for labels correctly' do
with_collection_radio_buttons :user, :name, ['$0.99', '$1.99'], :to_s, :to_s
assert_select 'label[for=user_name_099]', '$0.99'
assert_select 'label[for=user_name_199]', '$1.99'
@@ -194,7 +194,7 @@ class FormCollectionsHelperTest < ActionView::TestCase
assert_select 'label[for=user_active_no]', 'No'
end
- test 'colection check box should sanitize collection values for labels correctly' do
+ test 'collection check box should sanitize collection values for labels correctly' do
with_collection_check_boxes :user, :name, ['$0.99', '$1.99'], :to_s, :to_s
assert_select 'label[for=user_name_099]', '$0.99'
assert_select 'label[for=user_name_199]', '$1.99'
diff --git a/actionview/test/template/resolver_patterns_test.rb b/actionview/test/template/resolver_patterns_test.rb
index 97b1bad055..575eb9bd28 100644
--- a/actionview/test/template/resolver_patterns_test.rb
+++ b/actionview/test/template/resolver_patterns_test.rb
@@ -20,7 +20,7 @@ class ResolverPatternsTest < ActiveSupport::TestCase
assert_equal [:html], templates.first.formats
end
- def test_should_return_all_templates_when_ambigous_pattern
+ def test_should_return_all_templates_when_ambiguous_pattern
templates = @resolver.find_all("another", "custom_pattern", false, {:locale => [], :formats => [:html], :handlers => [:erb]})
assert_equal 2, templates.size, "expected two templates"
assert_equal "Another template!", templates[0].source
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index d512fa9913..e3440915a4 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -1,5 +1,6 @@
# encoding: utf-8
require 'abstract_unit'
+require 'minitest/mock'
class UrlHelperTest < ActiveSupport::TestCase
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index cc9483e67b..8b9ac97bbb 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -2,7 +2,9 @@ module ActiveModel
module SecurePassword
extend ActiveSupport::Concern
- class << self; attr_accessor :min_cost; end
+ class << self
+ attr_accessor :min_cost # :nodoc:
+ end
self.min_cost = false
module ClassMethods
diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb
index e4f5e61e91..bc185c737f 100644
--- a/activemodel/test/cases/serializers/json_serialization_test.rb
+++ b/activemodel/test/cases/serializers/json_serialization_test.rb
@@ -198,7 +198,7 @@ class JsonSerializationTest < ActiveModel::TestCase
assert_no_match %r{"preferences":}, json
end
- test "custom as_json options should be extendible" do
+ test "custom as_json options should be extensible" do
def @contact.as_json(options = {}); super(options.merge(only: [:name])); end
json = @contact.to_json
diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb
index 41a4c33727..5049d6dd61 100644
--- a/activemodel/test/cases/validations/conditional_validation_test.rb
+++ b/activemodel/test/cases/validations/conditional_validation_test.rb
@@ -23,7 +23,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: :condition_is_true)
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_if_validation_using_method_false
@@ -31,7 +31,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: :condition_is_true_but_its_not)
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_unless_validation_using_method_false
@@ -57,7 +57,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: "a = 1; a == 1")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_if_validation_using_string_false
@@ -65,7 +65,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: "false")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_unless_validation_using_string_false
@@ -93,7 +93,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
unless: Proc.new { |r| r.content.size > 4 })
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_if_validation_using_block_false
@@ -102,7 +102,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
if: Proc.new { |r| r.title != "uhohuhoh"})
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_unless_validation_using_block_false
@@ -124,7 +124,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
t = Topic.new
assert t.invalid?, "A topic without a title should not be valid"
- assert t.errors[:author_name].empty?, "A topic without an 'important' title should not require an author"
+ assert_empty t.errors[:author_name], "A topic without an 'important' title should not require an author"
t.title = "Just a title"
assert t.valid?, "A topic with a basic title should be valid"
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index a485feaf47..c346b5d93d 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,8 +1,27 @@
+* Setting a has_one association on a new record no longer causes an empty
+ transaction.
+
+ *Dylan Thacker-Smith*
+
+* Fix `AR::Relation#merge` sometimes failing to preserve `readonly(false)` flag.
+
+ *thedarkone*
+
+* Re-use `order` argument pre-processing for `reorder`.
+
+ *Paul Nikitochkin*
+
+* Fix PredicateBuilder so polymorphic association keys in `where` clause can
+ accept objects other than direct descendants of `ActiveRecord::Base` (decorated
+ models, for example).
+
+ *Mikhail Dieterle*
+
* PostgreSQL adapter recognizes negative money values formatted with
parentheses (eg. `($1.25) # => -1.25`)).
Fixes #11899.
- * Yves Senn*
+ *Yves Senn*
* Stop interpreting SQL 'string' columns as :string type because there is no
common STRING datatype in SQL.
@@ -18,7 +37,6 @@
Example:
record = User.new
-
record.logged_in_from_ip # is type of an inet or a cidr
# Before:
diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc
index c3ee34da55..ca1f2fd665 100644
--- a/activerecord/RUNNING_UNIT_TESTS.rdoc
+++ b/activerecord/RUNNING_UNIT_TESTS.rdoc
@@ -24,6 +24,7 @@ Simply executing <tt>bundle exec rake test</tt> is equivalent to the following:
$ bundle exec rake test_mysql2
$ bundle exec rake test_postgresql
$ bundle exec rake test_sqlite3
+ $ bundle exec rake test_sqlite3_mem
There should be tests available for each database backend listed in the {Config
File}[rdoc-label:label-Config+File]. (the exact set of available tests is
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 5ceda933f2..33cbafc6aa 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -164,7 +164,7 @@ module ActiveRecord
private
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
def association_instance_get(name)
- @association_cache[name.to_sym]
+ @association_cache[name]
end
# Set the specified association instance.
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 8eec4f56af..e1fa5225b5 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -50,8 +50,8 @@ module ActiveRecord
# Checks whether record is different to the current target, without loading it
def different_target?(record)
- if record.nil?
- owner[reflection.foreign_key]
+ if record.nil?
+ owner[reflection.foreign_key]
else
record.id != owner[reflection.foreign_key]
end
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 34de1a1f32..1059fc032d 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -17,7 +17,7 @@ module ActiveRecord::Associations::Builder
end
self.extensions = []
- VALID_OPTIONS = [:class_name, :foreign_key, :validate]
+ VALID_OPTIONS = [:class_name, :class, :foreign_key, :validate]
attr_reader :name, :scope, :options
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 228c500f0a..8744a57355 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -542,7 +542,8 @@ module ActiveRecord
def include_in_memory?(record)
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
- owner.send(reflection.through_reflection.name).any? { |source|
+ assoc = owner.association(reflection.through_reflection.name)
+ assoc.reader.any? { |source|
target = source.send(reflection.source_reflection.name)
target.respond_to?(:include?) ? target.include?(record) : target == record
} || target.include?(record)
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 607ed0da46..a3fcca8a27 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -125,7 +125,11 @@ module ActiveRecord
end
def foreign_key_present?
- owner.attribute_present?(reflection.association_primary_key)
+ if reflection.klass.primary_key
+ owner.attribute_present?(reflection.association_primary_key)
+ else
+ false
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index a74dd1cdab..56331bbb0b 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -140,7 +140,21 @@ module ActiveRecord
case method
when :destroy
- count = scope.destroy_all.length
+ if scope.klass.primary_key
+ count = scope.destroy_all.length
+ else
+ scope.to_a.each do |record|
+ record.run_callbacks :destroy
+ end
+
+ arel = scope.arel
+
+ stmt = Arel::DeleteManager.new arel.engine
+ stmt.from scope.klass.arel_table
+ stmt.wheres = arel.constraints
+
+ count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
+ end
when :nullify
count = scope.update_all(source_reflection.foreign_key => nil)
else
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 3ab1ea1ff4..0008600418 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -27,6 +27,8 @@ module ActiveRecord
return self.target if !(target || record)
if (target != record) || record.changed?
+ save &&= owner.persisted?
+
transaction_if(save) do
remove_target!(options[:dependent]) if target && !target.destroyed?
@@ -34,7 +36,7 @@ module ActiveRecord
set_owner_attributes(record)
set_inverse_instance(record)
- if owner.persisted? && save && !record.save
+ if save && !record.save
nullify_owner_attributes(record)
set_owner_attributes(target) if target
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
index b534569063..8024105472 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
@@ -62,7 +62,20 @@ module ActiveRecord
end
def extract_record(row)
- Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}]
+ # This code is performance critical as it is called per row.
+ # see: https://github.com/rails/rails/pull/12185
+ hash = {}
+
+ index = 0
+ length = column_names_with_alias.length
+
+ while index < length
+ column_name, alias_name = column_names_with_alias[index]
+ hash[column_name] = row[alias_name]
+ index += 1
+ end
+
+ hash
end
def record_id(row)
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 2317e34bc0..6cc9d6c079 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -85,9 +85,11 @@ module ActiveRecord
def initialize(records, associations, preload_scope = nil)
@records = Array.wrap(records).compact.uniq
@associations = Array.wrap(associations)
- @preload_scope = preload_scope || Relation.create(nil, nil)
+ @preload_scope = preload_scope || NULL_RELATION
end
+ NULL_RELATION = Struct.new(:values).new({})
+
def run
unless records.empty?
associations.each { |association| preload(association) }
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 0cc836f991..928da71eed 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -29,6 +29,10 @@ module ActiveRecord
end
def records_for(ids)
+ query_scope(ids)
+ end
+
+ def query_scope(ids)
scope.where(association_key.in(ids))
end
@@ -52,12 +56,9 @@ module ActiveRecord
raise NotImplementedError
end
- # We're converting to a string here because postgres will return the aliased association
- # key in a habtm as a string (for whatever reason)
def owners_by_key
@owners_by_key ||= owners.group_by do |owner|
- key = owner[owner_key_name]
- key && key.to_s
+ owner[owner_key_name]
end
end
@@ -71,27 +72,34 @@ module ActiveRecord
owners_map = owners_by_key
owner_keys = owners_map.keys.compact
- if klass.nil? || owner_keys.empty?
- records = []
- else
+ # Each record may have multiple owners, and vice-versa
+ records_by_owner = Hash[owners.map { |owner| [owner, []] }]
+
+ if klass && owner_keys.any?
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
# Make several smaller queries if necessary or make one query if the adapter supports it
sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
- records = sliced.flat_map { |slice| records_for(slice).to_a }
+ sliced.each { |slice|
+ records = records_for(slice)
+ caster = type_caster(records, association_key_name)
+ records.each do |record|
+ owner_key = caster.call record[association_key_name]
+
+ owners_map[owner_key].each do |owner|
+ records_by_owner[owner] << record
+ end
+ end
+ }
end
- # Each record may have multiple owners, and vice-versa
- records_by_owner = Hash[owners.map { |owner| [owner, []] }]
- records.each do |record|
- owner_key = record[association_key_name].to_s
-
- owners_map[owner_key].each do |owner|
- records_by_owner[owner] << record
- end
- end
records_by_owner
end
+ IDENTITY = lambda { |value| value }
+ def type_caster(results, name)
+ IDENTITY
+ end
+
def reflection_scope
@reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
end
diff --git a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb
index 9a3fada380..c042a44b21 100644
--- a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb
@@ -12,7 +12,7 @@ module ActiveRecord
# Unlike the other associations, we want to get a raw array of rows so that we can
# access the aliased column on the join table
def records_for(ids)
- scope = super
+ scope = query_scope ids
klass.connection.select_all(scope.arel, 'SQL', scope.bind_values)
end
@@ -40,6 +40,11 @@ module ActiveRecord
end
end
+ def type_caster(results, name)
+ caster = results.column_types.fetch(name, results.identity_type)
+ lambda { |value| caster.type_cast value }
+ end
+
def build_scope
super.joins(join).select(join_select)
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index de06931845..2c625cec04 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -27,17 +27,16 @@ module ActiveRecord
def through_records_by_owner
Preloader.new(owners, through_reflection.name, through_scope).run
- Hash[owners.map do |owner|
- through_records = Array.wrap(owner.send(through_reflection.name))
+ should_reset = (through_scope != through_reflection.klass.unscoped) ||
+ (reflection.options[:source_type] && through_reflection.collection?)
- # Dont cache the association - we would only be caching a subset
- if (through_scope != through_reflection.klass.unscoped) ||
- (reflection.options[:source_type] && through_reflection.collection?)
- owner.association(through_reflection.name).reset
- end
+ owners.each_with_object({}) do |owner, h|
+ association = owner.association through_reflection.name
+ h[owner] = Array(association.reader)
- [owner, through_records]
- end]
+ # Dont cache the association - we would only be caching a subset
+ association.reset if should_reset
+ end
end
def through_scope
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index 10238555f0..02dc464536 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -42,7 +42,6 @@ module ActiveRecord
scope.first.tap { |record| set_inverse_instance(record) }
end
- # Implemented by subclasses
def replace(record)
raise NotImplementedError, "Subclasses must implement a replace(record) method"
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 41b5a6e926..f168282ea3 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -51,7 +51,7 @@ module ActiveRecord
def create_time_zone_conversion_attribute?(name, column)
time_zone_aware_attributes &&
!self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
- [:datetime, :timestamp].include?(column.type)
+ (:datetime == column.type || :timestamp == column.type)
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index b06add096f..04e3dd49e7 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -18,6 +18,7 @@ require 'arel'
require 'active_record/errors'
require 'active_record/log_subscriber'
require 'active_record/explain_subscriber'
+require 'active_record/relation/delegation'
module ActiveRecord #:nodoc:
# = Active Record
@@ -290,6 +291,7 @@ module ActiveRecord #:nodoc:
extend Translation
extend DynamicMatchers
extend Explain
+ extend Delegation::DelegateCache
include Persistence
include ReadonlyAttributes
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 751655e61c..527d13b9b0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -139,7 +139,8 @@ module ActiveRecord
exec_cache(sql, binds)
types = {}
- result.fields.each_with_index do |fname, i|
+ fields = result.fields
+ fields.each_with_index do |fname, i|
ftype = result.ftype i
fmod = result.fmod i
types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod|
@@ -148,7 +149,7 @@ module ActiveRecord
}
end
- ret = ActiveRecord::Result.new(result.fields, result.values, types)
+ ret = ActiveRecord::Result.new(fields, result.values, types)
result.clear
return ret
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 40263b2a70..9a26e5df3f 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -436,9 +436,47 @@ module ActiveRecord
cattr_accessor :all_loaded_fixtures
self.all_loaded_fixtures = {}
+ class ClassCache
+ def initialize(class_names, config)
+ @class_names = class_names.stringify_keys
+ @config = config
+
+ # Remove string values that aren't constants or subclasses of AR
+ @class_names.delete_if { |k,klass|
+ unless klass.is_a? Class
+ klass = klass.safe_constantize
+ ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
+ end
+ !insert_class(@class_names, k, klass)
+ }
+ end
+
+ def [](fs_name)
+ @class_names.fetch(fs_name) {
+ klass = default_fixture_model(fs_name, @config).safe_constantize
+ insert_class(@class_names, fs_name, klass)
+ }
+ end
+
+ private
+
+ def insert_class(class_names, name, klass)
+ # We only want to deal with AR objects.
+ if klass && klass < ActiveRecord::Base
+ class_names[name] = klass
+ else
+ class_names[name] = nil
+ end
+ end
+
+ def default_fixture_model(fs_name, config)
+ ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
+ end
+ end
+
def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
fixture_set_names = Array(fixture_set_names).map(&:to_s)
- class_names = class_names.stringify_keys
+ class_names = ClassCache.new class_names, config
# FIXME: Apparently JK uses this.
connection = block_given? ? yield : ActiveRecord::Base.connection
@@ -452,10 +490,12 @@ module ActiveRecord
fixtures_map = {}
fixture_sets = files_to_read.map do |fs_name|
+ klass = class_names[fs_name]
+ conn = klass ? klass.connection : connection
fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
- connection,
+ conn,
fs_name,
- class_names[fs_name] || (default_fixture_model_name(fs_name, config).safe_constantize),
+ klass,
::File.join(fixtures_directory, fs_name))
end
@@ -500,10 +540,10 @@ module ActiveRecord
attr_reader :table_name, :name, :fixtures, :model_class, :config
def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
- @fixtures = {} # Ordered hash
@name = name
@path = path
@config = config
+ @model_class = nil
if class_name.is_a?(String)
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
@@ -515,14 +555,13 @@ module ActiveRecord
@model_class = class_name.safe_constantize if class_name
end
- @connection = ( model_class.respond_to?(:connection) ?
- model_class.connection : connection )
+ @connection = connection
@table_name = ( model_class.respond_to?(:table_name) ?
model_class.table_name :
self.class.default_fixture_table_name(name, config) )
- read_fixture_files
+ @fixtures = read_fixture_files path, @model_class
end
def [](x)
@@ -556,7 +595,7 @@ module ActiveRecord
rows[table_name] = fixtures.map do |label, fixture|
row = fixture.to_hash
- if model_class && model_class < ActiveRecord::Base
+ if model_class
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
if model_class.record_timestamps
timestamp_column_names.each do |c_name|
@@ -596,8 +635,12 @@ module ActiveRecord
row[fk_name] = ActiveRecord::FixtureSet.identify(value)
end
+ when :has_many
+ if association.options[:through]
+ add_join_records(rows, row, HasManyThroughProxy.new(association))
+ end
when :has_and_belongs_to_many
- handle_habtm(rows, row, association)
+ add_join_records(rows, row, HABTMProxy.new(association))
end
end
end
@@ -607,18 +650,56 @@ module ActiveRecord
rows
end
+ class ReflectionProxy # :nodoc:
+ def initialize(association)
+ @association = association
+ end
+
+ def join_table
+ @association.join_table
+ end
+
+ def name
+ @association.name
+ end
+ end
+
+ class HasManyThroughProxy < ReflectionProxy # :nodoc:
+ def rhs_key
+ @association.foreign_key
+ end
+
+ def lhs_key
+ @association.through_reflection.foreign_key
+ end
+ end
+
+ class HABTMProxy < ReflectionProxy # :nodoc:
+ def rhs_key
+ @association.association_foreign_key
+ end
+
+ def lhs_key
+ @association.foreign_key
+ end
+ end
+
private
def primary_key_name
@primary_key_name ||= model_class && model_class.primary_key
end
- def handle_habtm(rows, row, association)
+ def add_join_records(rows, row, association)
+ # This is the case when the join table has no fixtures file
if (targets = row.delete(association.name.to_s))
- targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
table_name = association.join_table
+ lhs_key = association.lhs_key
+ rhs_key = association.rhs_key
+
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
rows[table_name].concat targets.map { |target|
- { association.foreign_key => row[primary_key_name],
- association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
+ { lhs_key => row[primary_key_name],
+ rhs_key => ActiveRecord::FixtureSet.identify(target) }
}
end
end
@@ -641,12 +722,12 @@ module ActiveRecord
@column_names ||= @connection.columns(@table_name).collect { |c| c.name }
end
- def read_fixture_files
- yaml_files = Dir["#{@path}/{**,*}/*.yml"].select { |f|
+ def read_fixture_files(path, model_class)
+ yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
::File.file?(f)
- } + [yaml_file_path]
+ } + [yaml_file_path(path)]
- yaml_files.each do |file|
+ yaml_files.each_with_object({}) do |file, fixtures|
FixtureSet::File.open(file) do |fh|
fh.each do |fixture_name, row|
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
@@ -655,8 +736,8 @@ module ActiveRecord
end
end
- def yaml_file_path
- "#{@path}.yml"
+ def yaml_file_path(path)
+ "#{path}.yml"
end
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 23541d1d27..75c0c1bda8 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -224,13 +224,20 @@ module ActiveRecord
def decorate_columns(columns_hash) # :nodoc:
return if columns_hash.empty?
- columns_hash.each do |name, col|
- if serialized_attributes.key?(name)
- columns_hash[name] = AttributeMethods::Serialization::Type.new(col)
- end
- if create_time_zone_conversion_attribute?(name, col)
- columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(col)
- end
+ @serialized_column_names ||= self.columns_hash.keys.find_all do |name|
+ serialized_attributes.key?(name)
+ end
+
+ @serialized_column_names.each do |name|
+ columns_hash[name] = AttributeMethods::Serialization::Type.new(columns_hash[name])
+ end
+
+ @time_zone_column_names ||= self.columns_hash.find_all do |name, col|
+ create_time_zone_conversion_attribute?(name, col)
+ end.map!(&:first)
+
+ @time_zone_column_names.each do |name|
+ columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(columns_hash[name])
end
columns_hash
@@ -284,16 +291,19 @@ module ActiveRecord
undefine_attribute_methods
connection.schema_cache.clear_table_cache!(table_name) if table_exists?
- @arel_engine = nil
- @column_defaults = nil
- @column_names = nil
- @columns = nil
- @columns_hash = nil
- @column_types = nil
- @content_columns = nil
- @dynamic_methods_hash = nil
- @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
- @relation = nil
+ @arel_engine = nil
+ @column_defaults = nil
+ @column_names = nil
+ @columns = nil
+ @columns_hash = nil
+ @column_types = nil
+ @content_columns = nil
+ @dynamic_methods_hash = nil
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
+ @relation = nil
+ @serialized_column_names = nil
+ @time_zone_column_names = nil
+ @cached_time_zone = nil
end
# This is a hook for use by modules that need to do extra stuff to
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index f428f160cf..150a90f5db 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -121,6 +121,7 @@ module ActiveRecord
@scope = scope
@options = options
@active_record = active_record
+ @klass = options[:class]
@plural_name = active_record.pluralize_table_names ?
name.to_s.pluralize : name.to_s
end
@@ -422,7 +423,6 @@ module ActiveRecord
def valid_inverse_reflection?(reflection)
reflection &&
klass.name == reflection.active_record.name &&
- klass.primary_key == reflection.active_record_primary_key &&
can_find_inverse_of_automatically?(reflection)
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 52a538e5b5..27c04b0952 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -161,8 +161,7 @@ module ActiveRecord
result = result.map do |attributes|
values = klass.initialize_attributes(attributes).values
- iter = columns.each
- values.map { |value| iter.next.type_cast value }
+ columns.zip(values).map { |column, value| column.type_cast value }
end
columns.one? ? result.map!(&:first) : result
end
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index b6f80ac5c7..e28938f9d0 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,8 +1,33 @@
-require 'thread'
-require 'thread_safe'
+require 'active_support/concern'
module ActiveRecord
module Delegation # :nodoc:
+ module DelegateCache
+ def relation_delegate_class(klass) # :nodoc:
+ @relation_delegate_cache[klass]
+ end
+
+ def initialize_relation_delegate_cache # :nodoc:
+ @relation_delegate_cache = cache = {}
+ [
+ ActiveRecord::Relation,
+ ActiveRecord::Associations::CollectionProxy,
+ ActiveRecord::AssociationRelation
+ ].each do |klass|
+ delegate = Class.new(klass) {
+ include ClassSpecificRelation
+ }
+ const_set klass.name.gsub('::', '_'), delegate
+ cache[klass] = delegate
+ end
+ end
+
+ def inherited(child_class)
+ child_class.initialize_relation_delegate_cache
+ super
+ end
+ end
+
extend ActiveSupport::Concern
# This module creates compiled delegation methods dynamically at runtime, which makes
@@ -71,32 +96,14 @@ module ActiveRecord
end
module ClassMethods # :nodoc:
- @@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2)
-
def create(klass, *args)
relation_class_for(klass).new(klass, *args)
end
private
- # Cache the constants in @@subclasses because looking them up via const_get
- # make instantiation significantly slower.
+
def relation_class_for(klass)
- if klass && (klass_name = klass.name)
- my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new }
- # This hash is keyed by klass.name to avoid memory leaks in development mode
- my_cache.compute_if_absent(klass_name) do
- # Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name
- subclass_name = "#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}"
-
- if const_defined?(subclass_name)
- const_get(subclass_name)
- else
- const_set(subclass_name, Class.new(self) { include ClassSpecificRelation })
- end
- end
- else
- ActiveRecord::Relation
- end
+ klass.relation_delegate_class(self)
end
end
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index c08158d38b..c05632e688 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -58,7 +58,11 @@ module ActiveRecord
def merge
normal_values.each do |name|
value = values[name]
- relation.send("#{name}!", *value) unless value.blank?
+ # The unless clause is here mostly for performance reasons (since the `send` call might be moderately
+ # expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
+ # `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
+ # don't fall through the cracks.
+ relation.send("#{name}!", *value) unless value.nil? || (value.blank? && false != value)
end
merge_multi_values
@@ -107,11 +111,11 @@ module ActiveRecord
bind_values = filter_binds(lhs_binds, removed) + rhs_binds
conn = relation.klass.connection
- bviter = bind_values.each.with_index
+ bv_index = 0
where_values.map! do |node|
if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right
- (column, _), i = bviter.next
- substitute = conn.substitute_at column, i
+ substitute = conn.substitute_at(bind_values[bv_index].first, bv_index)
+ bv_index += 1
Arel::Nodes::Equality.new(node.left, substitute)
else
node
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 8948f2bba5..c60cd27a83 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -55,7 +55,7 @@ module ActiveRecord
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(estimate_of: treasure)
- if klass && value.class < Base && reflection = klass.reflect_on_association(column.to_sym)
+ if klass && value.is_a?(Base) && reflection = klass.reflect_on_association(column.to_sym)
if reflection.polymorphic?
queries << build(table[reflection.foreign_type], value.class.base_class)
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 9f2a039d94..1c6ea94c0b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -289,17 +289,7 @@ module ActiveRecord
end
def order!(*args) # :nodoc:
- args.flatten!
- validate_order_args(args)
-
- references = args.grep(String)
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
- references!(references) if references.any?
-
- # if a symbol is given we prepend the quoted table name
- args.map! do |arg|
- arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg
- end
+ preprocess_order_args(args)
self.order_values += args
self
@@ -320,8 +310,7 @@ module ActiveRecord
end
def reorder!(*args) # :nodoc:
- args.flatten!
- validate_order_args(args)
+ preprocess_order_args(args)
self.reordering_value = true
self.order_values = args
@@ -1036,6 +1025,20 @@ module ActiveRecord
end
end
+ def preprocess_order_args(order_args)
+ order_args.flatten!
+ validate_order_args(order_args)
+
+ references = order_args.grep(String)
+ references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
+ references!(references) if references.any?
+
+ # if a symbol is given we prepend the quoted table name
+ order_args.map! do |arg|
+ arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg
+ end
+ end
+
# Checks to make sure that the arguments are not blank. Note that if some
# blank-like object were initially passed into the query method, then this
# method will not raise an error.
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 253368ae5b..d0f1cb5b75 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -93,7 +93,21 @@ module ActiveRecord
# used as keys in ActiveRecord::Base's @attributes hash
columns = @columns.map { |c| c.dup.freeze }
@rows.map { |row|
- Hash[columns.zip(row)]
+ # In the past we used Hash[columns.zip(row)]
+ # though elegant, the verbose way is much more efficient
+ # both time and memory wise cause it avoids a big array allocation
+ # this method is called a lot and needs to be micro optimised
+ hash = {}
+
+ index = 0
+ length = columns.length
+
+ while index < length
+ hash[columns[index]] = row[index]
+ index += 1
+ end
+
+ hash
}
end
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index a5d6aad3f0..01fec31544 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -100,11 +100,7 @@ module ActiveRecord
elsif default_scopes.any?
evaluate_default_scope do
default_scopes.inject(relation) do |default_scope, scope|
- if !scope.is_a?(Relation) && scope.respond_to?(:call)
- default_scope.merge(unscoped { scope.call })
- else
- default_scope.merge(scope)
- end
+ default_scope.merge(unscoped { scope.call })
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index a534f0e56a..1122f8b9a1 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -52,6 +52,11 @@ module ActiveRecord
c = Column.new(nil, nil, 'text')
assert_equal "'666'", @conn.quote(fixnum, c)
end
+
+ def test_quote_time_usec
+ assert_equal "'1970-01-01 00:00:00.000000'", @conn.quote(Time.at(0))
+ assert_equal "'1970-01-01 00:00:00.000000'", @conn.quote(Time.at(0).to_datetime)
+ end
end
end
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index e693d34f99..811d91f849 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -52,12 +52,10 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_cascaded_eager_association_loading_with_join_for_count
categories = Category.joins(:categorizations).includes([{:posts=>:comments}, :authors])
- assert_nothing_raised do
- assert_equal 4, categories.count
- assert_equal 4, categories.to_a.count
- assert_equal 3, categories.distinct.count
- assert_equal 3, categories.to_a.uniq.size # Must uniq since instantiating with inner joins will get dupes
- end
+ assert_equal 4, categories.count
+ assert_equal 4, categories.to_a.count
+ assert_equal 3, categories.distinct.count
+ assert_equal 3, categories.to_a.uniq.size # Must uniq since instantiating with inner joins will get dupes
end
def test_cascaded_eager_association_loading_with_duplicated_includes
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 28bf48f4fd..8d3d6962fc 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1172,8 +1172,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
}
end
- test "works in combination with order(:symbol)" do
- author = Author.includes(:posts).references(:posts).order(:name).where('posts.title IS NOT NULL').first
+ test "works in combination with order(:symbol) and reorder(:symbol)" do
+ author = Author.includes(:posts).references(:posts).order(:name).find_by('posts.title IS NOT NULL')
+ assert_equal authors(:bob), author
+
+ author = Author.includes(:posts).references(:posts).reorder(:name).find_by('posts.title IS NOT NULL')
assert_equal authors(:bob), author
end
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 712a770133..bb927841ab 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -605,6 +605,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_join_table_alias
+ # FIXME: `references` has no impact on the aliases generated for the join
+ # query. The fact that we pass `:developers_projects_join` to `references`
+ # and that the SQL string contains `developers_projects_join` is merely a
+ # coincidence.
assert_equal(
3,
Developer.references(:developers_projects_join).merge(
@@ -615,6 +619,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_join_with_group
+ # FIXME: `references` has no impact on the aliases generated for the join
+ # query. The fact that we pass `:developers_projects_join` to `references`
+ # and that the SQL string contains `developers_projects_join` is merely a
+ # coincidence.
group = Developer.columns.inject([]) do |g, c|
g << "developers.#{c.name}"
g << "developers_projects_2.#{c.name}"
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ce51853bf3..4c0fa88917 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -45,6 +45,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
Client.destroyed_client_ids.clear
end
+ def test_anonymous_has_many
+ developer = Class.new(ActiveRecord::Base) {
+ self.table_name = 'developers'
+ dev = self
+
+ developer_project = Class.new(ActiveRecord::Base) {
+ self.table_name = 'developers_projects'
+ belongs_to :developer, :class => dev
+ }
+ has_many :developer_projects, :class => developer_project, :foreign_key => 'developer_id'
+ }
+ dev = developer.first
+ named = Developer.find(dev.id)
+ assert_operator dev.developer_projects.count, :>, 0
+ assert_equal named.projects.map(&:id).sort,
+ dev.developer_projects.map(&:project_id).sort
+ end
+
def test_create_from_association_should_respect_default_scope
car = Car.create(:name => 'honda')
assert_equal 'honda', car.name
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 941e851aae..dd8b426b25 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -28,7 +28,8 @@ require 'models/club'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags,
:owners, :pets, :toys, :jobs, :references, :companies, :members, :author_addresses,
- :subscribers, :books, :subscriptions, :developers, :categorizations, :essays
+ :subscribers, :books, :subscriptions, :developers, :categorizations, :essays,
+ :categories_posts
# Dummies to force column loads so query counts are clean.
def setup
@@ -36,6 +37,101 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
Reader.create :person_id => 0, :post_id => 0
end
+ def make_model(name)
+ Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
+ end
+
+ def test_singleton_has_many_through
+ book = make_model "Book"
+ subscription = make_model "Subscription"
+ subscriber = make_model "Subscriber"
+
+ subscriber.primary_key = 'nick'
+ subscription.belongs_to :book, class: book
+ subscription.belongs_to :subscriber, class: subscriber
+
+ book.has_many :subscriptions, class: subscription
+ book.has_many :subscribers, through: :subscriptions, class: subscriber
+
+ anonbook = book.first
+ namebook = Book.find anonbook.id
+
+ assert_operator anonbook.subscribers.count, :>, 0
+ anonbook.subscribers.each do |s|
+ assert_instance_of subscriber, s
+ end
+ assert_equal namebook.subscribers.map(&:id).sort,
+ anonbook.subscribers.map(&:id).sort
+ end
+
+ def test_no_pk_join_table_append
+ lesson, _, student = make_no_pk_hm_t
+
+ sicp = lesson.new(:name => "SICP")
+ ben = student.new(:name => "Ben Bitdiddle")
+ sicp.students << ben
+ assert sicp.save!
+ end
+
+ def test_no_pk_join_table_delete
+ lesson, lesson_student, student = make_no_pk_hm_t
+
+ sicp = lesson.new(:name => "SICP")
+ ben = student.new(:name => "Ben Bitdiddle")
+ louis = student.new(:name => "Louis Reasoner")
+ sicp.students << ben
+ sicp.students << louis
+ assert sicp.save!
+
+ sicp.students.reload
+ assert_operator lesson_student.count, :>=, 2
+ assert_no_difference('student.count') do
+ assert_difference('lesson_student.count', -2) do
+ sicp.students.destroy(*student.all.to_a)
+ end
+ end
+ end
+
+ def test_no_pk_join_model_callbacks
+ lesson, lesson_student, student = make_no_pk_hm_t
+
+ after_destroy_called = false
+ lesson_student.after_destroy do
+ after_destroy_called = true
+ end
+
+ sicp = lesson.new(:name => "SICP")
+ ben = student.new(:name => "Ben Bitdiddle")
+ sicp.students << ben
+ assert sicp.save!
+
+ sicp.students.reload
+ sicp.students.destroy(*student.all.to_a)
+ assert after_destroy_called, "after destroy should be called"
+ end
+
+ def make_no_pk_hm_t
+ lesson = make_model 'Lesson'
+ student = make_model 'Student'
+
+ lesson_student = make_model 'LessonStudent'
+ lesson_student.table_name = 'lessons_students'
+
+ lesson_student.belongs_to :lesson, :class => lesson
+ lesson_student.belongs_to :student, :class => student
+ lesson.has_many :lesson_students, :class => lesson_student
+ lesson.has_many :students, :through => :lesson_students, :class => student
+ [lesson, lesson_student, student]
+ end
+
+ def test_pk_is_not_required_for_join
+ post = Post.includes(:scategories).first
+ post2 = Post.includes(:categories).first
+
+ assert_operator post.categories.length, :>, 0
+ assert_equal post2.categories, post.categories
+ end
+
def test_include?
person = Person.new
post = Post.new
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 4fdf9a9643..9cd4db8dc9 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -505,6 +505,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_no_queries { company.account = nil }
account = Account.find(2)
assert_queries { company.account = account }
+
+ assert_no_queries { Firm.new.account = account }
end
def test_has_one_assignment_triggers_save_on_change
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 9b1abc3b81..cf3c07845c 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -371,7 +371,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
prev_default_scope = Club.default_scopes
[:includes, :preload, :joins, :eager_load].each do |q|
- Club.default_scopes = [Club.send(q, :category)]
+ Club.default_scopes = [proc { Club.send(q, :category) }]
assert_equal categories(:general), members(:groucho).reload.club_category
end
ensure
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index c91cf89f6d..dc2de7b523 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1324,6 +1324,35 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal 1, post.comments.length
end
+ def test_marshal_between_processes
+ skip "fork isn't supported" unless Process.respond_to?(:fork)
+
+ # Define a new model to ensure there are no caches
+ if self.class.const_defined?("Post", false)
+ flunk "there should be no post constant"
+ end
+
+ self.class.const_set("Post", Class.new(ActiveRecord::Base) {
+ has_many :comments
+ })
+
+ rd, wr = IO.pipe
+
+ ActiveRecord::Base.connection_handler.clear_all_connections!
+
+ fork do
+ rd.close
+ post = Post.new
+ post.comments.build
+ wr.write Marshal.dump(post)
+ wr.close
+ end
+
+ wr.close
+ assert Marshal.load rd.read
+ rd.close
+ end
+
def test_marshalling_new_record_round_trip_with_associations
post = Post.new
post.comments.build
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index f73e449610..1e6ccecfab 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -126,7 +126,7 @@ module ActiveRecord
def test_dup_with_default_scope
prev_default_scopes = Topic.default_scopes
- Topic.default_scopes = [Topic.where(:approved => true)]
+ Topic.default_scopes = [proc { Topic.where(:approved => true) }]
topic = Topic.new(:approved => false)
assert !topic.dup.approved?, "should not be overridden by default scopes"
ensure
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 949e994f8d..1b9ef14ec9 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -51,6 +51,7 @@ class FinderTest < ActiveRecord::TestCase
assert_equal true, Topic.exists?(heading: "The First Topic")
assert_equal true, Topic.exists?(:author_name => "Mary", :approved => true)
assert_equal true, Topic.exists?(["parent_id = ?", 1])
+ assert_equal true, Topic.exists?(id: [1, 9999])
assert_equal false, Topic.exists?(45)
assert_equal false, Topic.exists?(Topic.new)
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 4e877aed2e..e61deb1080 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -84,6 +84,12 @@ class FixturesTest < ActiveRecord::TestCase
assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
end
+ def test_create_symbol_fixtures_is_deprecated
+ assert_deprecated do
+ ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :collections, :collections => 'Course') { Course.connection }
+ end
+ end
+
def test_attributes
topics = create_fixtures("topics").first
assert_equal("The First Topic", topics["first"]["title"])
@@ -263,6 +269,41 @@ class FixturesTest < ActiveRecord::TestCase
end
end
+class HasManyThroughFixture < ActiveSupport::TestCase
+ def make_model(name)
+ Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
+ end
+
+ def test_has_many_through
+ pt = make_model "ParrotTreasure"
+ parrot = make_model "Parrot"
+ treasure = make_model "Treasure"
+
+ pt.table_name = "parrots_treasures"
+ pt.belongs_to :parrot, :class => parrot
+ pt.belongs_to :treasure, :class => treasure
+
+ parrot.has_many :parrot_treasures, :class => pt
+ parrot.has_many :treasures, :through => :parrot_treasures
+
+ parrots = File.join FIXTURES_ROOT, 'parrots'
+
+ fs = ActiveRecord::FixtureSet.new parrot.connection, "parrots", parrot, parrots
+ rows = fs.table_rows
+ assert_equal load_has_and_belongs_to_many['parrots_treasures'], rows['parrots_treasures']
+ end
+
+ def load_has_and_belongs_to_many
+ parrot = make_model "Parrot"
+ parrot.has_and_belongs_to_many :treasures
+
+ parrots = File.join FIXTURES_ROOT, 'parrots'
+
+ fs = ActiveRecord::FixtureSet.new parrot.connection, "parrots", parrot, parrots
+ fs.table_rows
+ end
+end
+
if Account.connection.respond_to?(:reset_pk_sequence!)
class FixturesResetPkSequenceTest < ActiveRecord::TestCase
fixtures :accounts
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index d333be3560..3e460fa3d6 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -81,6 +81,31 @@ module ActiveRecord
assert_equal expected.to_sql, actual.to_sql
end
+ def test_decorated_polymorphic_where
+ treasure_decorator = Struct.new(:model) do
+ def self.method_missing(method, *args, &block)
+ Treasure.send(method, *args, &block)
+ end
+
+ def is_a?(klass)
+ model.is_a?(klass)
+ end
+
+ def method_missing(method, *args, &block)
+ model.send(method, *args, &block)
+ end
+ end
+
+ treasure = Treasure.new
+ treasure.id = 1
+ decorated_treasure = treasure_decorator.new(treasure)
+
+ expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: 1)
+ actual = PriceEstimate.where(estimate_of: decorated_treasure)
+
+ assert_equal expected.to_sql, actual.to_sql
+ end
+
def test_aliased_attribute
expected = Topic.where(heading: 'The First Topic')
actual = Topic.where(title: 'The First Topic')
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index f99801c437..e77de19ac3 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -9,6 +9,10 @@ module ActiveRecord
fixtures :posts, :comments, :authors
class FakeKlass < Struct.new(:table_name, :name)
+ extend ActiveRecord::Delegation::DelegateCache
+
+ inherited self
+
def self.connection
Post.connection
end
@@ -76,8 +80,8 @@ module ActiveRecord
end
def test_table_name_delegates_to_klass
- relation = Relation.new FakeKlass.new('foo'), :b
- assert_equal 'foo', relation.table_name
+ relation = Relation.new FakeKlass.new('posts'), :b
+ assert_equal 'posts', relation.table_name
end
def test_scope_for_create
@@ -177,14 +181,26 @@ module ActiveRecord
end
test 'merging a hash interpolates conditions' do
- klass = stub_everything
- klass.stubs(:sanitize_sql).with(['foo = ?', 'bar']).returns('foo = bar')
+ klass = Class.new(FakeKlass) do
+ def self.sanitize_sql(args)
+ raise unless args == ['foo = ?', 'bar']
+ 'foo = bar'
+ end
+ end
relation = Relation.new(klass, :b)
relation.merge!(where: ['foo = ?', 'bar'])
assert_equal ['foo = bar'], relation.where_values
end
+ def test_merging_readonly_false
+ relation = Relation.new FakeKlass, :b
+ readonly_false_relation = relation.readonly(false)
+ # test merging in both directions
+ assert_equal false, relation.merge(readonly_false_relation).readonly_value
+ assert_equal false, readonly_false_relation.merge(relation).readonly_value
+ end
+
def test_relation_merging_with_merged_joins_as_symbols
special_comments_with_ratings = SpecialComment.joins(:ratings)
posts_with_special_comments_with_ratings = Post.group("posts.id").joins(:special_comments).merge(special_comments_with_ratings)
@@ -210,6 +226,9 @@ module ActiveRecord
class RelationMutationTest < ActiveSupport::TestCase
class FakeKlass < Struct.new(:table_name, :name)
+ extend ActiveRecord::Delegation::DelegateCache
+ inherited self
+
def arel_table
Post.arel_table
end
@@ -217,6 +236,10 @@ module ActiveRecord
def connection
Post.connection
end
+
+ def relation_delegate_class(klass)
+ self.class.relation_delegate_class(klass)
+ end
end
def relation
@@ -296,6 +319,15 @@ module ActiveRecord
assert relation.reordering_value
end
+ test '#reorder! with symbol prepends the table name' do
+ assert relation.reorder!(:name).equal?(relation)
+ node = relation.order_values.first
+
+ assert node.ascending?
+ assert_equal :name, node.expr.name
+ assert_equal "posts", node.expr.relation.name
+ end
+
test 'reverse_order!' do
assert relation.reverse_order!.equal?(relation)
assert relation.reverse_order_value
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index e1a760d240..b3ca45c4c8 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1344,6 +1344,24 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [], scope.references_values
end
+ def test_automatically_added_reorder_references
+ scope = Post.reorder('comments.body')
+ assert_equal %w(comments), scope.references_values
+
+ scope = Post.reorder('comments.body', 'yaks.body')
+ assert_equal %w(comments yaks), scope.references_values
+
+ # Don't infer yaks, let's not go down that road again...
+ scope = Post.reorder('comments.body, yaks.body')
+ assert_equal %w(comments), scope.references_values
+
+ scope = Post.reorder('comments.body asc')
+ assert_equal %w(comments), scope.references_values
+
+ scope = Post.reorder('foo(comments.body)')
+ assert_equal [], scope.references_values
+ end
+
def test_presence
topics = Topic.all
diff --git a/activerecord/test/fixtures/tasks.yml b/activerecord/test/fixtures/tasks.yml
index 402ca85faf..c38b32b0e5 100644
--- a/activerecord/test/fixtures/tasks.yml
+++ b/activerecord/test/fixtures/tasks.yml
@@ -1,4 +1,4 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
first_task:
id: 1
starting: 2005-03-30t06:30:00.00+01:00
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index feb828de31..7dad8041f3 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -8,9 +8,7 @@ class Author < ActiveRecord::Base
has_many :posts_sorted_by_id_limited, -> { order('posts.id').limit(1) }, :class_name => "Post"
has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post"
has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post"
- has_many :posts_containing_the_letter_a, :class_name => "Post"
has_many :posts_with_special_categorizations, :class_name => 'PostWithSpecialCategorization'
- has_many :posts_with_extension, :class_name => "Post"
has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
has_many :comments, through: :posts do
@@ -32,7 +30,6 @@ class Author < ActiveRecord::Base
has_many :welcome_posts, -> { where(:title => 'Welcome to the weblog') }, :class_name => 'Post'
has_many :comments_desc, -> { order('comments.id DESC') }, :through => :posts, :source => :comments
- has_many :limited_comments, -> { limit(1) }, :through => :posts, :source => :comments
has_many :funky_comments, :through => :posts, :source => :comments
has_many :ordered_uniq_comments, -> { distinct.order('comments.id') }, :through => :posts, :source => :comments
has_many :ordered_uniq_comments_desc, -> { distinct.order('comments.id DESC') }, :through => :posts, :source => :comments
diff --git a/activerecord/test/models/auto_id.rb b/activerecord/test/models/auto_id.rb
index d720e2be5e..82c6544bd5 100644
--- a/activerecord/test/models/auto_id.rb
+++ b/activerecord/test/models/auto_id.rb
@@ -1,4 +1,4 @@
class AutoId < ActiveRecord::Base
- def self.table_name () "auto_id_tests" end
- def self.primary_key () "auto_id" end
+ self.table_name = "auto_id_tests"
+ self.primary_key = "auto_id"
end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index a14a9febba..6d257dbe7e 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,12 +1,9 @@
class Car < ActiveRecord::Base
-
has_many :bulbs
has_many :funky_bulbs, class_name: 'FunkyBulb', dependent: :destroy
has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb"
- has_many :frickinawesome_bulbs, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_one :bulb
- has_one :frickinawesome_bulb, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_many :tyres
has_many :engines, :dependent => :destroy
diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb
index 545aa8110d..3d87eb795c 100644
--- a/activerecord/test/models/citation.rb
+++ b/activerecord/test/models/citation.rb
@@ -1,6 +1,3 @@
class Citation < ActiveRecord::Base
belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id
-
- belongs_to :book1, :class_name => "Book", :foreign_key => :book1_id
- belongs_to :book2, :class_name => "Book", :foreign_key => :book2_id
end
diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb
index 816c5e6937..566e0873f1 100644
--- a/activerecord/test/models/club.rb
+++ b/activerecord/test/models/club.rb
@@ -2,7 +2,6 @@ class Club < ActiveRecord::Base
has_one :membership
has_many :memberships, :inverse_of => false
has_many :members, :through => :memberships
- has_many :current_memberships
has_one :sponsor
has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member"
belongs_to :category
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index c5d4ec0833..8104c607b5 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -49,7 +49,6 @@ class Firm < Company
has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client"
has_many :plain_clients, :class_name => 'Client'
- has_many :readonly_clients, -> { readonly }, :class_name => 'Client'
has_many :clients_using_primary_key, :class_name => 'Client',
:primary_key => 'name', :foreign_key => 'firm_name'
has_many :clients_using_primary_key_with_delete_all, :class_name => 'Client',
@@ -167,7 +166,6 @@ class ExclusivelyDependentFirm < Company
has_one :account, :foreign_key => "firm_id", :dependent => :delete
has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
- has_many :dependent_hash_conditional_clients_of_firm, -> { order("id").where(:name => 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
end
class SpecialClient < Client
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index cc47c7bc18..72095f9236 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -2,7 +2,6 @@ class Member < ActiveRecord::Base
has_one :current_membership
has_one :selected_membership
has_one :membership
- has_many :fellow_members, :through => :club, :source => :members
has_one :club, :through => :current_membership
has_one :selected_club, :through => :selected_membership, :source => :club
has_one :favourite_club, -> { where "memberships.favourite = ?", true }, :through => :membership, :source => :club
diff --git a/activerecord/test/models/movie.rb b/activerecord/test/models/movie.rb
index 6384b4c801..c441be2bef 100644
--- a/activerecord/test/models/movie.rb
+++ b/activerecord/test/models/movie.rb
@@ -1,5 +1,3 @@
class Movie < ActiveRecord::Base
- def self.primary_key
- "movieid"
- end
+ self.primary_key = "movieid"
end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 93a7a2073c..452d54580a 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -1,4 +1,10 @@
class Post < ActiveRecord::Base
+ class CategoryPost < ActiveRecord::Base
+ self.table_name = "categories_posts"
+ belongs_to :category
+ belongs_to :post
+ end
+
module NamedExtension
def author
'lifo'
@@ -72,6 +78,8 @@ class Post < ActiveRecord::Base
has_many :special_comments_ratings, :through => :special_comments, :source => :ratings
has_many :special_comments_ratings_taggings, :through => :special_comments_ratings, :source => :taggings
+ has_many :category_posts, :class_name => 'CategoryPost'
+ has_many :scategories, through: :category_posts, source: :category
has_and_belongs_to_many :categories
has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id'
@@ -122,7 +130,6 @@ class Post < ActiveRecord::Base
has_many :secure_readers
has_many :readers_with_person, -> { includes(:person) }, :class_name => "Reader"
has_many :people, :through => :readers
- has_many :secure_people, :through => :secure_readers
has_many :single_people, :through => :readers
has_many :people_with_callbacks, :source=>:person, :through => :readers,
:before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) },
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index c094b726b4..7f42a4b1f8 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -1,7 +1,6 @@
class Project < ActiveRecord::Base
has_and_belongs_to_many :developers, -> { distinct.order 'developers.name desc, developers.id desc' }
has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer"
- has_and_belongs_to_many :selected_developers, -> { distinct.select "developers.*" }, :class_name => "Developer"
has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer'
has_and_belongs_to_many :limited_developers, -> { limit 1 }, :class_name => "Developer"
has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, :class_name => "Developer"
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 17035bf338..40c8e97fc2 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -34,7 +34,6 @@ class Topic < ActiveRecord::Base
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
has_many :approved_replies, -> { approved }, class_name: 'Reply', foreign_key: "parent_id", counter_cache: 'replies_count'
- has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title"
has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 81c6246c5c..f15297f279 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Ensure that autoloaded constants in all-caps nestings are marked as
+ autoloaded.
+
+ *Simon Coffey*
+
* Add String#remove(pattern) as a short-hand for the common pattern of String#gsub(pattern, '')
*DHH*
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index b979521c99..34ac91334a 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -124,7 +124,6 @@ module ActiveSupport
protected
- # See https://gist.github.com/ssimeonov/6047200
PER_ENTRY_OVERHEAD = 240
def cached_size(key, entry)
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 5c738572a8..c3aac31323 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -574,7 +574,7 @@ module ActiveSupport
#
# set_callback :save, :before_meth
#
- # The callback can specified as a symbol naming an instance method; as a
+ # The callback can be specified as a symbol naming an instance method; as a
# proc, lambda, or block; as a string to be instance evaluated; or as an
# object that responds to a certain method determined by the <tt>:scope</tt>
# argument to +define_callback+.
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 534bbe3c42..48190e1e66 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -47,7 +47,7 @@ class Object
end
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
- # does not implemented the tried method.
+ # does not implement the tried method.
def try!(*a, &b)
if a.empty? && block_given?
yield self
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 73559bfe0e..db9f5d4baa 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -459,7 +459,7 @@ module ActiveSupport #:nodoc:
if loaded.include?(expanded)
raise "Circular dependency detected while autoloading constant #{qualified_name}"
else
- require_or_load(expanded)
+ require_or_load(expanded, qualified_name)
raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
return from_mod.const_get(const_name)
end
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 00727ef73c..87b6407038 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -70,12 +70,10 @@ module ActiveSupport
alias :until :ago
def inspect #:nodoc:
- val_for = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
- [:years, :months, :days, :minutes, :seconds].
- select {|unit| val_for[unit].nonzero?}.
- tap {|units| units << :seconds if units.empty?}.
- map {|unit| [unit, val_for[unit]]}.
- map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
+ parts.
+ reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
+ sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
+ map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
to_sentence(:locale => :en)
end
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index d063fcb49c..ffdb7b53c4 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -36,7 +36,7 @@ module ActiveSupport
# string.
#
# If passed an optional +locale+ parameter, the word will be
- # pluralized using rules defined for that language. By default,
+ # singularized using rules defined for that language. By default,
# this parameter is set to <tt>:en</tt>.
#
# 'posts'.singularize # => "post"
diff --git a/activesupport/test/autoloading_fixtures/html/some_class.rb b/activesupport/test/autoloading_fixtures/html/some_class.rb
new file mode 100644
index 0000000000..b43d15d891
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/html/some_class.rb
@@ -0,0 +1,4 @@
+module HTML
+ class SomeClass
+ end
+end
diff --git a/activesupport/test/core_ext/bigdecimal_test.rb b/activesupport/test/core_ext/bigdecimal_test.rb
index a5987044b9..b386e55d6c 100644
--- a/activesupport/test/core_ext/bigdecimal_test.rb
+++ b/activesupport/test/core_ext/bigdecimal_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'bigdecimal'
require 'active_support/core_ext/big_decimal'
class BigDecimalTest < ActiveSupport::TestCase
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 5e3987265b..ed267cf4b9 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -27,6 +27,7 @@ class DurationTest < ActiveSupport::TestCase
def test_equals
assert 1.day == 1.day
assert 1.day == 1.day.to_i
+ assert 1.day.to_i == 1.day
assert !(1.day == 'foo')
end
@@ -37,6 +38,8 @@ class DurationTest < ActiveSupport::TestCase
assert_equal '6 months and -2 days', (6.months - 2.days).inspect
assert_equal '10 seconds', 10.seconds.inspect
assert_equal '10 years, 2 months, and 1 day', (10.years + 2.months + 1.day).inspect
+ assert_equal '10 years, 2 months, and 1 day', (10.years + 1.month + 1.day + 1.month).inspect
+ assert_equal '10 years, 2 months, and 1 day', (1.day + 10.years + 2.months).inspect
assert_equal '7 days', 1.week.inspect
assert_equal '14 days', 1.fortnight.inspect
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 2537af0394..3cb66d4eec 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -11,13 +11,6 @@ require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/indent'
-module Ace
- module Base
- class Case
- end
- end
-end
-
class StringInflectionsTest < ActiveSupport::TestCase
include InflectorTestCases
include ConstantizeTestCases
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 68b6cc6e8c..9b84e75902 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -647,6 +647,14 @@ class DependenciesTest < ActiveSupport::TestCase
Object.class_eval { remove_const :E }
end
+ def test_constants_in_capitalized_nesting_marked_as_autoloaded
+ with_autoloading_fixtures do
+ ActiveSupport::Dependencies.load_missing_constant(HTML, "SomeClass")
+
+ assert ActiveSupport::Dependencies.autoloaded?("HTML::SomeClass")
+ end
+ end
+
def test_unloadable
with_autoloading_fixtures do
Object.const_set :M, Module.new
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index ce91c443e1..e0f85f4e7c 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -3,7 +3,6 @@ require 'abstract_unit'
require 'active_support/inflector/transliterate'
class TransliterateTest < ActiveSupport::TestCase
-
def test_transliterate_should_not_change_ascii_chars
(0..127).each do |byte|
char = [byte].pack("U")
@@ -24,12 +23,13 @@ class TransliterateTest < ActiveSupport::TestCase
def test_transliterate_should_work_with_custom_i18n_rules_and_uncomposed_utf8
char = [117, 776].pack("U*") # "ü" as ASCII "u" plus COMBINING DIAERESIS
I18n.backend.store_translations(:de, :i18n => {:transliterate => {:rule => {"ü" => "ue"}}})
- I18n.locale = :de
+ default_locale, I18n.locale = I18n.locale, :de
assert_equal "ue", ActiveSupport::Inflector.transliterate(char)
+ ensure
+ I18n.locale = default_locale
end
def test_transliterate_should_allow_a_custom_replacement_char
assert_equal "a*b", ActiveSupport::Inflector.transliterate("a索b", "*")
end
-
end
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index a025279e16..d992028323 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -106,7 +106,11 @@ module XmlMiniTest
module Nokogiri end
setup do
- @xml = ActiveSupport::XmlMini
+ @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend
+ end
+
+ teardown do
+ ActiveSupport::XmlMini.backend = @default_backend
end
test "#with_backend should switch backend and then switch back" do
@@ -135,7 +139,11 @@ module XmlMiniTest
module LibXML end
setup do
- @xml = ActiveSupport::XmlMini
+ @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend
+ end
+
+ teardown do
+ ActiveSupport::XmlMini.backend = @default_backend
end
test "#with_backend should be thread-safe" do
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index 63fbe27aed..a8868a1877 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -25,7 +25,7 @@ class Comment < ActiveRecord::Base
belongs_to :post
end
-class BugTest < Minitest::Test
+class BugTest < MiniTest::Unit::TestCase
def test_association_stuff
post = Post.create!
post.comments << Comment.create!
diff --git a/guides/code/getting_started/test/fixtures/comments.yml b/guides/code/getting_started/test/fixtures/comments.yml
index 0cd36069e4..9e409d8a61 100644
--- a/guides/code/getting_started/test/fixtures/comments.yml
+++ b/guides/code/getting_started/test/fixtures/comments.yml
@@ -1,4 +1,4 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
commenter: MyString
diff --git a/guides/code/getting_started/test/fixtures/posts.yml b/guides/code/getting_started/test/fixtures/posts.yml
index 617a24b858..46b01c3bb4 100644
--- a/guides/code/getting_started/test/fixtures/posts.yml
+++ b/guides/code/getting_started/test/fixtures/posts.yml
@@ -1,4 +1,4 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
title: MyString
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index ff4d30b4c0..e070ef6031 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -266,9 +266,9 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ
* `find_all_by_...` can be rewritten using `where(...)`.
* `find_last_by_...` can be rewritten using `where(...).last`.
* `scoped_by_...` can be rewritten using `where(...)`.
- * `find_or_initialize_by_...` can be rewritten using `where(...).first_or_initialize`.
- * `find_or_create_by_...` can be rewritten using `find_or_create_by(...)` or `where(...).first_or_create`.
- * `find_or_create_by_...!` can be rewritten using `find_or_create_by!(...)` or `where(...).first_or_create!`.
+ * `find_or_initialize_by_...` can be rewritten using `find_or_initialize_by(...)`.
+ * `find_or_create_by_...` can be rewritten using `find_or_create_by(...)`.
+ * `find_or_create_by_...!` can be rewritten using `find_or_create_by!(...)`.
Credits
-------
diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb
index 9210c40c17..0a0a958e30 100644
--- a/guides/source/_welcome.html.erb
+++ b/guides/source/_welcome.html.erb
@@ -15,7 +15,7 @@
</p>
<% end %>
<p>
- The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.13/">http://guides.rubyonrails.org/v3.2.13/</a>.
+ The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.14/">http://guides.rubyonrails.org/v3.2.14/</a>.
</p>
<p>
The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>.
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 8dcd544a52..8dfecd0190 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -808,11 +808,11 @@ class AdminsController < ApplicationController
private
- def authenticate
- authenticate_or_request_with_http_digest do |username|
- USERS[username]
+ def authenticate
+ authenticate_or_request_with_http_digest do |username|
+ USERS[username]
+ end
end
- end
end
```
@@ -839,13 +839,13 @@ class ClientsController < ApplicationController
private
- def generate_pdf(client)
- Prawn::Document.new do
- text client.name, align: :center
- text "Address: #{client.address}"
- text "Email: #{client.email}"
- end.render
- end
+ def generate_pdf(client)
+ Prawn::Document.new do
+ text client.name, align: :center
+ text "Address: #{client.address}"
+ text "Email: #{client.email}"
+ end.render
+ end
end
```
@@ -1048,9 +1048,9 @@ class ApplicationController < ActionController::Base
private
- def record_not_found
- render text: "404 Not Found", status: 404
- end
+ def record_not_found
+ render text: "404 Not Found", status: 404
+ end
end
```
@@ -1062,10 +1062,10 @@ class ApplicationController < ActionController::Base
private
- def user_not_authorized
- flash[:error] = "You don't have access to this section."
- redirect_to :back
- end
+ def user_not_authorized
+ flash[:error] = "You don't have access to this section."
+ redirect_to :back
+ end
end
class ClientsController < ApplicationController
@@ -1079,10 +1079,10 @@ class ClientsController < ApplicationController
private
- # If the user is not authorized, just throw the exception.
- def check_authorization
- raise User::NotAuthorized unless current_user.admin?
- end
+ # If the user is not authorized, just throw the exception.
+ def check_authorization
+ raise User::NotAuthorized unless current_user.admin?
+ end
end
```
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index bf34799eb3..93a2b89ede 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -569,25 +569,25 @@ class UserMailer < ActionMailer::Base
private
- def set_delivery_options
- # You have access to the mail instance,
- # @business and @user instance variables here
- if @business && @business.has_smtp_settings?
- mail.delivery_method.settings.merge!(@business.smtp_settings)
+ def set_delivery_options
+ # You have access to the mail instance,
+ # @business and @user instance variables here
+ if @business && @business.has_smtp_settings?
+ mail.delivery_method.settings.merge!(@business.smtp_settings)
+ end
end
- end
- def prevent_delivery_to_guests
- if @user && @user.guest?
- mail.perform_deliveries = false
+ def prevent_delivery_to_guests
+ if @user && @user.guest?
+ mail.perform_deliveries = false
+ end
end
- end
- def set_business_headers
- if @business
- headers["X-SMTPAPI-CATEGORY"] = @business.code
+ def set_business_headers
+ if @business
+ headers["X-SMTPAPI-CATEGORY"] = @business.code
+ end
end
- end
end
```
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 95eb84dd1f..aa2ce99f6d 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -35,11 +35,11 @@ class User < ActiveRecord::Base
before_validation :ensure_login_has_a_value
protected
- def ensure_login_has_a_value
- if login.nil?
- self.login = email unless email.blank?
+ def ensure_login_has_a_value
+ if login.nil?
+ self.login = email unless email.blank?
+ end
end
- end
end
```
@@ -65,13 +65,13 @@ class User < ActiveRecord::Base
after_validation :set_location, on: [ :create, :update ]
protected
- def normalize_name
- self.name = self.name.downcase.titleize
- end
+ def normalize_name
+ self.name = self.name.downcase.titleize
+ end
- def set_location
- self.location = LocationService.query(self)
- end
+ def set_location
+ self.location = LocationService.query(self)
+ end
end
```
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index ba0dc6d9eb..5aa935a155 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1466,7 +1466,7 @@ Client.pluck(:id, :name)
# => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
```
-`pluck` makes it possible to replace code like
+`pluck` makes it possible to replace code like:
```ruby
Client.select(:id).map { |c| c.id }
@@ -1476,7 +1476,7 @@ Client.select(:id).map(&:id)
Client.select(:id, :name).map { |c| [c.id, c.name] }
```
-with
+with:
```ruby
Client.pluck(:id)
@@ -1484,6 +1484,37 @@ Client.pluck(:id)
Client.pluck(:id, :name)
```
+Unlike `select`, `pluck` directly converts a database result into a Ruby `Array`,
+without constructing `ActiveRecord` objects. This can mean better performance for
+a large or often-running query. However, any model method overrides will
+not be available. For example:
+
+```ruby
+class Client < ActiveRecord::Base
+ def name
+ "I am #{super}"
+ end
+end
+
+Client.select(:name).map &:name
+# => ["I am David", "I am Jeremy", "I am Jose"]
+
+Client.pluck(:name)
+# => ["David", "Jeremy", "Jose"]
+```
+
+Furthermore, unlike `select` and other `Relation` scopes, `pluck` triggers an immediate
+query, and thus cannot be chained with any further scopes, although it can work with
+scopes already constructed earlier:
+
+```ruby
+Client.pluck(:name).limit(1)
+# => NoMethodError: undefined method `limit' for #<Array:0x007ff34d3ad6d8>
+
+Client.limit(1).pluck(:name)
+# => ["David"]
+```
+
### `ids`
`ids` can be used to pluck all the IDs for the relation using the table's primary key.
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index ca023f7f66..fa32fc898e 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -2736,14 +2736,14 @@ NOTE: Defined in `active_support/core_ext/hash/except.rb`.
The method `transform_keys` accepts a block and returns a hash that has applied the block operations to each of the keys in the receiver:
```ruby
-{nil => nil, 1 => 1, a: :a}.transform_keys{ |key| key.to_s.upcase }
+{nil => nil, 1 => 1, a: :a}.transform_keys { |key| key.to_s.upcase }
# => {"" => nil, "A" => :a, "1" => 1}
```
The result in case of collision is undefined:
```ruby
-{"a" => 1, a: 2}.transform_keys{ |key| key.to_s.upcase }
+{"a" => 1, a: 2}.transform_keys { |key| key.to_s.upcase }
# => {"A" => 2}, in my test, can't rely on this result though
```
@@ -2751,11 +2751,11 @@ This method may be useful for example to build specialized conversions. For inst
```ruby
def stringify_keys
- transform_keys{ |key| key.to_s }
+ transform_keys { |key| key.to_s }
end
...
def symbolize_keys
- transform_keys{ |key| key.to_sym rescue key }
+ transform_keys { |key| key.to_sym rescue key }
end
```
@@ -2764,7 +2764,7 @@ There's also the bang variant `transform_keys!` that applies the block operation
Besides that, one can use `deep_transform_keys` and `deep_transform_keys!` to perform the block operation on all the keys in the given hash and all the hashes nested into it. An example of the result is:
```ruby
-{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_transform_keys{ |key| key.to_s.upcase }
+{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_transform_keys { |key| key.to_s.upcase }
# => {""=>nil, "1"=>1, "NESTED"=>{"A"=>3, "5"=>5}}
```
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 8fa86cb516..72aff1e0dd 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -765,8 +765,8 @@ headers.
For Apache:
```apache
-# The Expires* directives requires the Apache module `mod_expires` to be
-# enabled.
+# The Expires* directives requires the Apache module
+# `mod_expires` to be enabled.
<Location /assets/>
# Use of ETag is discouraged when Last-Modified is present
Header unset ETag FileETag None
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 3cf6631c32..d3d25f4d4e 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -225,7 +225,7 @@ This is the default cache store implementation.
### ActiveSupport::Cache::MemCacheStore
-This cache store uses Danga's `memcached` server to provide a centralized cache for your application. Rails uses the bundled `dalli` gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy.
+This cache store uses Danga's `memcached` server to provide a centralized cache for your application. Rails uses the bundled `dalli` gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very high performance and redundancy.
When initializing the cache, you need to specify the addresses for all memcached servers in your cluster. If none is specified, it will assume memcached is running on the local host on the default port, but this is not an ideal set up for larger sites.
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 639476eeeb..f11e08a0c1 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -471,7 +471,7 @@ spec/models/user_spec.rb:
INFO: A good description of unit testing in Rails is given in [A Guide to Testing Rails Applications](testing.html)
-Rails comes with a test suite called `Test::Unit`. Rails owes its stability to the use of tests. The tasks available in the `test:` namespace helps in running the different tests you will hopefully write.
+Rails comes with a test suite called Minitest. Rails owes its stability to the use of tests. The tasks available in the `test:` namespace helps in running the different tests you will hopefully write.
### `tmp`
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index 14d65e4747..3099ccae97 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -386,7 +386,7 @@ Finally, to see where you are in the code again you can type `list=`
7
8 respond_to do |format|
9 format.html # index.html.erb
- 10 format.json { render :json => @posts }
+ 10 format.json { render json: @posts }
```
### The Context
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 12b192fa87..c4e5789a1a 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -86,7 +86,6 @@ And if you are on Fedora or CentOS, you're done with
$ sudo yum install sqlite3 sqlite3-devel
```
-<<<<<<< HEAD
If you are on Arch Linux, you will need to run:
```bash
@@ -101,8 +100,6 @@ For FreeBSD users, you're done with:
Or compile the `databases/sqlite3` port.
-=======
->>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4
Get a recent version of [Bundler](http://gembundler.com/)
```bash
@@ -158,13 +155,20 @@ $ cd railties
$ TEST_DIR=generators bundle exec rake test
```
-You can run any single test separately too:
+You can run the tests for a particular file by using:
```bash
$ cd actionpack
$ bundle exec ruby -Itest test/template/form_helper_test.rb
```
+Or, you can run a single test in a particular file:
+
+```bash
+$ cd actionpack
+$ bundle exec ruby -Itest path/to/test.rb -n test_name
+```
+
### Active Record Setup
The test suite of Active Record attempts to run four times: once for SQLite3, once for each of the two MySQL gems (`mysql` and `mysql2`), and once for PostgreSQL. We are going to see now how to set up the environment for them.
@@ -191,7 +195,6 @@ $ sudo yum install mysql-server mysql-devel
$ sudo yum install postgresql-server postgresql-devel
```
-<<<<<<< HEAD
If you are running Arch Linux, MySQL isn't supported anymore so you will need to
use MariaDB instead (see [this announcement](https://www.archlinux.org/news/mariadb-replaces-mysql-in-repositories/)):
@@ -211,8 +214,6 @@ Or install them through ports (they are located under the `databases` folder).
If you run into troubles during the installation of MySQL, please see
[the MySQL documentation](http://dev.mysql.com/doc/refman/5.1/en/freebsd-installation.html).
-=======
->>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4
After that, run:
```bash
diff --git a/guides/source/engines.md b/guides/source/engines.md
index bc404ccb7f..ec51fb9234 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -399,9 +399,9 @@ def create
end
private
-def comment_params
- params.require(:comment).permit(:text)
-end
+ def comment_params
+ params.require(:comment).permit(:text)
+ end
```
This is the final part required to get the new comment form working. Displaying the comments however, is not quite right yet. If you were to create a comment right now you would see this error:
@@ -850,7 +850,6 @@ module Blorgh::Concerns::Models::Post
before_save :set_author
private
-
def set_author
self.author = User.find_or_create_by(name: author_name)
end
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index 578cfbe105..39852cb00a 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -914,9 +914,9 @@ def create
end
private
-def person_params
- params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
-end
+ def person_params
+ params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
+ end
```
### Removing Objects
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 5b1758a771..81e57aee34 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -573,6 +573,41 @@ Finally, we redirect the user to the `show` action, which we'll define later.
TIP: As we'll see later, `@post.save` returns a boolean indicating
whether the model was saved or not.
+If you now go to
+<http://localhost:3000/posts/new> you'll *almost* be able to create a post. Try
+it! You should get an error that looks like this:
+
+![Forbidden attributes for new post](images/getting_started/forbidden_attributes_for_new_post.png)
+
+Rails has several security features that help you write secure applications,
+and you're running into one of them now. This one is called
+`strong_parameters`, which requires us to tell Rails exactly which parameters
+we want to accept in our controllers. In this case, we want to allow the
+`title` and `text` parameters, so change your `create` controller action to
+look like this:
+
+```ruby
+def create
+ @post = Post.new(post_params)
+
+ @post.save
+ redirect_to @post
+end
+
+private
+ def post_params
+ params.require(:post).permit(:title, :text)
+ end
+```
+
+See the `permit`? It allows us to accept both `title` and `text` in this
+action.
+
+TIP: Note that `def post_params` is private. This new approach prevents an attacker from
+setting the model's attributes by manipulating the hash passed to the model.
+For more information, refer to
+[this blog post about Strong Parameters](http://weblog.rubyonrails.org/2012/3/21/strong-parameters/).
+
### Showing Posts
If you submit the form again now, Rails will complain about not finding
@@ -618,44 +653,11 @@ content:
</p>
```
-If you now go to
-<http://localhost:3000/posts/new> you'll *almost* be able to create a post. Try
-it! You should get an error that looks like this:
-
-![Forbidden attributes for new post](images/getting_started/forbidden_attributes_for_new_post.png)
-
-Rails has several security features that help you write secure applications,
-and you're running into one of them now. This one is called
-`strong_parameters`, which requires us to tell Rails exactly which parameters
-we want to accept in our controllers. In this case, we want to allow the
-`title` and `text` parameters, so change your `create` controller action to
-look like this:
-
-```
-def create
- @post = Post.new(post_params)
-
- @post.save
- redirect_to @post
-end
-
-private
- def post_params
- params.require(:post).permit(:title, :text)
- end
-```
-
-See the `permit`? It allows us to accept both `title` and `text` in this
-action. With this change, you should finally be able to create new posts.
+With this change, you should finally be able to create new posts.
Visit <http://localhost:3000/posts/new> and give it a try!
![Show action for posts](images/getting_started/show_action_for_posts.png)
-TIP: Note that `def post_params` is private. This new approach prevents an attacker from
-setting the model's attributes by manipulating the hash passed to the model.
-For more information, refer to
-[this blog post about Strong Parameters](http://weblog.rubyonrails.org/2012/3/21/strong-parameters/).
-
### Listing all posts
We still need a way to list all our posts, so let's do that.
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index ead9c6b94d..c191db1df9 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -28,7 +28,7 @@ After reading this guide, you will know:
--------------------------------------------------------------------------------
-NOTE: The Ruby I18n framework provides you with all necessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See the Rails [I18n Wiki](http://rails-i18n.org/wiki) for more information.
+NOTE: The Ruby I18n framework provides you with all necessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See the Ruby [I18n Wiki](http://ruby-i18n.org/wiki) for more information.
How I18n in Ruby on Rails Works
-------------------------------
@@ -282,10 +282,11 @@ def set_locale
I18n.locale = extract_locale_from_accept_language_header
logger.debug "* Locale set to '#{I18n.locale}'"
end
+
private
-def extract_locale_from_accept_language_header
- request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
-end
+ def extract_locale_from_accept_language_header
+ request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
+ end
```
Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's [http_accept_language](https://github.com/iain/http_accept_language/tree/master) or even Rack middleware such as Ryan Tomayko's [locale](https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb).
@@ -742,7 +743,7 @@ en:
other: Dudes
```
-Then `User.model_name.human(:count => 2)` will return "Dudes". With `:count => 1` or without params will return "Dude".
+Then `User.model_name.human(count: 2)` will return "Dudes". With `count: 1` or without params will return "Dude".
#### Error Message Scopes
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index c78eef5bf0..91d12b4432 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -217,12 +217,12 @@ With the `default_options` set to this:
```ruby
def default_options
{
- :environment => ENV['RACK_ENV'] || "development",
- :pid => nil,
- :Port => 9292,
- :Host => "0.0.0.0",
- :AccessLog => [],
- :config => "config.ru"
+ environment: ENV['RACK_ENV'] || "development",
+ pid: nil,
+ Port: 9292,
+ Host: "0.0.0.0",
+ AccessLog: [],
+ config: "config.ru"
}
end
```
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index 6100fc89c8..414ce46a4e 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -692,10 +692,15 @@ Neither of these Rake tasks do anything you could not do with `db:migrate`. They
are simply more convenient, since you do not need to explicitly specify the
version to migrate to.
+### Setup the Database
+
+The `rake db:setup` task will create the database, load the schema and initialize
+it with the seed data.
+
### Resetting the Database
-The `rake db:reset` task will drop the database, recreate it and load the
-current schema into it.
+The `rake db:reset` task will drop the database and set it up again. This is
+functionally equivalent to `rake db:drop db:setup`.
NOTE: This is not the same as running all the migrations. It will only use the
contents of the current schema.rb file. If a migration can't be rolled back,
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 76c4c25108..37525c48a6 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -863,8 +863,8 @@ resources :user_permissions, controller: 'admin/user_permissions'
This will route to the `Admin::UserPermissions` controller.
NOTE: Only the directory notation is supported. Specifying the
-controller with Ruby constant notation (eg. `:controller =>
-'Admin::UserPermissions'`) can lead to routing problems and results in
+controller with Ruby constant notation (eg. `controller: 'Admin::UserPermissions'`)
+can lead to routing problems and results in
a warning.
### Specifying Constraints
diff --git a/guides/source/security.md b/guides/source/security.md
index 97b7355771..4aba39f55a 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -481,7 +481,7 @@ A good password is a long alphanumeric combination of mixed cases. As this is qu
INFO: _A common pitfall in Ruby's regular expressions is to match the string's beginning and end by ^ and $, instead of \A and \z._
-Ruby uses a slightly different approach than many other languages to match the end and the beginning of a string. That is why even many Ruby and Rails books make this wrong. So how is this a security threat? Say you wanted to loosely validate a URL field and you used a simple regular expression like this:
+Ruby uses a slightly different approach than many other languages to match the end and the beginning of a string. That is why even many Ruby and Rails books get this wrong. So how is this a security threat? Say you wanted to loosely validate a URL field and you used a simple regular expression like this:
```ruby
/^https?:\/\/[^\n]+$/i
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 1b0a0abf9a..5b8829b89a 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -757,24 +757,24 @@ class UserFlowsTest < ActionDispatch::IntegrationTest
private
- module CustomDsl
- def browses_site
- get "/products/all"
- assert_response :success
- assert assigns(:products)
+ module CustomDsl
+ def browses_site
+ get "/products/all"
+ assert_response :success
+ assert assigns(:products)
+ end
end
- end
- def login(user)
- open_session do |sess|
- sess.extend(CustomDsl)
- u = users(user)
- sess.https!
- sess.post "/login", username: u.username, password: u.password
- assert_equal '/welcome', path
- sess.https!(false)
+ def login(user)
+ open_session do |sess|
+ sess.extend(CustomDsl)
+ u = users(user)
+ sess.https!
+ sess.post "/login", username: u.username, password: u.password
+ assert_equal '/welcome', path
+ sess.https!(false)
+ end
end
- end
end
```
@@ -887,10 +887,9 @@ class PostsControllerTest < ActionController::TestCase
private
- def initialize_post
- @post = posts(:one)
- end
-
+ def initialize_post
+ @post = posts(:one)
+ end
end
```
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 79e0c6bf09..357918a73c 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -184,6 +184,12 @@ this gem such as `whitelist_attributes` or `mass_assignment_sanitizer` options.
* `find_or_initialize_by_...` becomes `find_or_initialize_by(...)`.
* `find_or_create_by_...` becomes `find_or_create_by(...)`.
+* Note that `where(...)` returns a relation, not an array like the old finders. If you require an `Array`, use `where(...).to_a`.
+
+* These equivalent methods may not execute the same SQL as the previous implementation.
+
+* To re-enable the old finders, you can use the [activerecord-deprecated_finders gem](https://github.com/rails/activerecord-deprecated_finders).
+
### Active Resource
Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your Gemfile.
diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb
index 1f9568fb5f..3e32576040 100644
--- a/railties/lib/rails/api/task.rb
+++ b/railties/lib/rails/api/task.rb
@@ -16,8 +16,7 @@ module Rails
:include => %w(
README.rdoc
lib/active_record/**/*.rb
- ),
- :exclude => 'lib/active_record/vendor/*'
+ )
},
'activemodel' => {
@@ -33,23 +32,22 @@ module Rails
lib/abstract_controller/**/*.rb
lib/action_controller/**/*.rb
lib/action_dispatch/**/*.rb
- ),
- :exclude => 'lib/action_controller/vendor/*'
+ )
},
'actionview' => {
:include => %w(
README.rdoc
lib/action_view/**/*.rb
- )
+ ),
+ :exclude => 'lib/action_view/vendor/*'
},
'actionmailer' => {
:include => %w(
README.rdoc
lib/action_mailer/**/*.rb
- ),
- :exclude => 'lib/action_mailer/vendor/*'
+ )
},
'railties' => {
diff --git a/railties/lib/rails/app_rails_loader.rb b/railties/lib/rails/app_rails_loader.rb
index 4a17803f1c..fbb83fa10e 100644
--- a/railties/lib/rails/app_rails_loader.rb
+++ b/railties/lib/rails/app_rails_loader.rb
@@ -2,7 +2,7 @@ require 'pathname'
module Rails
module AppRailsLoader
- RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
+ RUBY = Gem.ruby
EXECUTABLES = ['bin/rails', 'script/rails']
BUNDLER_WARNING = <<EOS
Looks like your app's ./bin/rails is a stub that was generated by Bundler.
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index f25f629aa5..e8adef2fd3 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -351,8 +351,13 @@ module Rails
Rails::Railtie::Configuration.eager_load_namespaces << base
base.called_from = begin
- # Remove the line number from backtraces making sure we don't leave anything behind
- call_stack = caller.map { |p| p.sub(/:\d+.*/, '') }
+ call_stack = if Kernel.respond_to?(:caller_locations)
+ caller_locations.map(&:path)
+ else
+ # Remove the line number from backtraces making sure we don't leave anything behind
+ caller.map { |p| p.sub(/:\d+.*/, '') }
+ end
+
File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] })
end
end
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
index 595256f36a..9969a1475d 100644
--- a/railties/lib/rails/engine/railties.rb
+++ b/railties/lib/rails/engine/railties.rb
@@ -16,8 +16,6 @@ module Rails
def -(others)
_all - others
end
-
- delegate :engines, to: "self.class"
end
end
end
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 7e938fab47..8aec8bc8f9 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -168,15 +168,15 @@ module Rails
as_hook = options.delete(:as) || generator_name
names.each do |name|
- defaults = if options[:type] == :boolean
- { }
- elsif [true, false].include?(default_value_for_option(name, options))
- { banner: "" }
- else
- { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
- end
-
unless class_options.key?(name)
+ defaults = if options[:type] == :boolean
+ { }
+ elsif [true, false].include?(default_value_for_option(name, options))
+ { banner: "" }
+ else
+ { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
+ end
+
class_option(name, defaults.merge!(options))
end
@@ -255,12 +255,7 @@ module Rails
# Split the class from its module nesting
nesting = class_name.split('::')
last_name = nesting.pop
-
- # Extract the last Module in the nesting
- last = nesting.inject(Object) do |last_module, nest|
- break unless last_module.const_defined?(nest, false)
- last_module.const_get(nest)
- end
+ last = extract_last_module(nesting)
if last && last.const_defined?(last_name.camelize, false)
raise Error, "The name '#{class_name}' is either already used in your application " <<
@@ -270,6 +265,14 @@ module Rails
end
end
+ # Takes in an array of nested modules and extracts the last module
+ def extract_last_module(nesting)
+ nesting.inject(Object) do |last_module, nest|
+ break unless last_module.const_defined?(nest, false)
+ last_module.const_get(nest)
+ end
+ end
+
# Use Rails default banner.
def self.banner
"rails generate #{namespace.sub(/^rails:/,'')} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]".gsub(/\s+/, ' ')
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 9fb8ea6955..041bfcb733 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -68,7 +68,7 @@ module Rails
directory "bin" do |content|
"#{shebang}\n" + content
end
- chmod "bin", 0755, verbose: false
+ chmod "bin", 0755 & ~File.umask, verbose: false
end
def config
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 822f35fb42..ef84447df9 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -32,18 +32,18 @@ module Rails
# namespace :foo do
# namespace :bar do
namespace_ladder = class_path.each_with_index.map do |ns, i|
- %{#{" " * i * 2}namespace :#{ns} do\n }
+ indent("namespace :#{ns} do\n", i * 2)
end.join
# Create route
# get "baz/index"
- route = %{#{" " * depth * 2}get "#{file_name}/#{action}"\n}
+ route = indent(%{get "#{file_name}/#{action}"\n}, depth * 2)
# Create `end` ladder
# end
# end
end_ladder = (1..depth).reverse_each.map do |i|
- "#{" " * i * 2}end\n"
+ indent("end\n", i * 2)
end.join
# Combine the 3 parts to generate complete route entry
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 0cbbf04da2..eb3b2d8ef4 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -80,7 +80,7 @@ module Rails
to_prepare_blocks << blk if blk
end
- def respond_to?(name)
+ def respond_to?(name, include_private = false)
super || @@options.key?(name.to_sym)
end
diff --git a/railties/test/application/basic_rendering_test.rb b/railties/test/application/basic_rendering_test.rb
deleted file mode 100644
index 00ba433a05..0000000000
--- a/railties/test/application/basic_rendering_test.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'isolation/abstract_unit'
-require 'rack/test'
-
-module ApplicationTests
- class BasicRenderingTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::Isolation
- include Rack::Test::Methods
-
- def setup
- build_app
- end
-
- def teardown
- teardown_app
- end
-
- test "Rendering without ActionView" do
- gsub_app_file 'config/application.rb', "require 'rails/all'", <<-RUBY
- require "active_model/railtie"
- require "action_controller/railtie"
- RUBY
-
- # Turn off ActionView and jquery-rails (it depends on AV)
- $:.reject! {|path| path =~ /(actionview|jquery\-rails)/ }
- boot_rails
-
- app_file 'app/controllers/pages_controller.rb', <<-RUBY
- class PagesController < ApplicationController
- def render_hello_world
- render text: "Hello World!"
- end
-
- def render_nothing
- render nothing: true
- end
-
- def no_render; end
-
- def raise_error
- render foo: "bar"
- end
- end
- RUBY
-
- get '/pages/render_hello_world'
- assert_equal 200, last_response.status
- assert_equal "Hello World!", last_response.body
- assert_equal "text/plain; charset=utf-8", last_response.content_type
-
- get '/pages/render_nothing'
- assert_equal 200, last_response.status
- assert_equal " ", last_response.body
- assert_equal "text/plain; charset=utf-8", last_response.content_type
-
- get '/pages/no_render'
- assert_equal 500, last_response.status
-
- get '/pages/raise_error'
- assert_equal 500, last_response.status
- end
- end
-end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index c51488e0e1..03a735b1c1 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -679,5 +679,12 @@ module ApplicationTests
end
assert_equal Logger::INFO, Rails.logger.level
end
+
+ test "respond_to? accepts include_private" do
+ make_basic_app
+
+ assert_not Rails.configuration.respond_to?(:method_missing)
+ assert Rails.configuration.respond_to?(:method_missing, true)
+ end
end
end
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index a4d8b3d1b0..e17925ff65 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -44,7 +44,7 @@ class NamespacedControllerGeneratorTest < NamespacedGeneratorTestCase
end
end
- def test_helpr_is_also_namespaced
+ def test_helper_is_also_namespaced
run_generator
assert_file "app/helpers/test_app/account_helper.rb", /module TestApp/, / module AccountHelper/
assert_file "test/helpers/test_app/account_helper_test.rb", /module TestApp/, / class AccountHelperTest/