aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/lib/action_mailer/base.rb9
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb2
-rw-r--r--actionmailer/lib/action_mailer/version.rb3
-rw-r--r--actionpack/CHANGELOG.md16
-rw-r--r--actionpack/lib/abstract_controller/base.rb2
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb44
-rw-r--r--actionpack/lib/action_controller/metal.rb15
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb13
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb20
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb5
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb10
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb76
-rw-r--r--actionpack/lib/action_dispatch/journey/visualizer/index.html.erb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb4
-rw-r--r--actionpack/lib/action_dispatch/routing/endpoint.rb10
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb15
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb91
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb18
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb49
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb1
-rw-r--r--actionpack/test/abstract/callbacks_test.rb8
-rw-r--r--actionpack/test/controller/filters_test.rb380
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb4
-rw-r--r--actionpack/test/controller/routing_test.rb10
-rw-r--r--actionpack/test/controller/test_case_test.rb2
-rw-r--r--actionpack/test/dispatch/routing_test.rb9
-rw-r--r--actionpack/test/dispatch/session/mem_cache_store_test.rb18
-rw-r--r--actionpack/test/dispatch/url_generation_test.rb18
-rw-r--r--actionpack/test/journey/router_test.rb51
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb51
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb30
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb4
-rw-r--r--actionview/lib/action_view/rendering.rb4
-rw-r--r--activerecord/CHANGELOG.md49
-rw-r--r--activerecord/lib/active_record/associations.rb13
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb12
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb4
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb6
-rw-r--r--activerecord/lib/active_record/autosave_association.rb55
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb81
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb79
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb134
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb271
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/type.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/binary.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/boolean.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/date.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/date_time.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/decimal.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/float.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/integer.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/string.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/text.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/time.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/value.rb17
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/counter_cache.rb7
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/model_schema.rb18
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb4
-rw-r--r--activerecord/lib/active_record/properties.rb107
-rw-r--r--activerecord/lib/active_record/reflection.rb52
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb11
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb4
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb27
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb12
-rw-r--r--activerecord/lib/active_record/validations/presence.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb1
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb6
-rw-r--r--activerecord/test/cases/associations/eager_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb16
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb10
-rw-r--r--activerecord/test/cases/base_test.rb12
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb8
-rw-r--r--activerecord/test/cases/calculations_test.rb4
-rw-r--r--activerecord/test/cases/column_test.rb159
-rw-r--r--activerecord/test/cases/connection_adapters/type/type_map_test.rb10
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb16
-rw-r--r--activerecord/test/cases/custom_properties_test.rb64
-rw-r--r--activerecord/test/cases/helper.rb1
-rw-r--r--activerecord/test/cases/migration_test.rb8
-rw-r--r--activerecord/test/cases/primary_keys_test.rb32
-rw-r--r--activerecord/test/cases/quoting_test.rb28
-rw-r--r--activerecord/test/cases/reflection_test.rb7
-rw-r--r--activerecord/test/cases/relation/where_chain_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb14
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb7
-rw-r--r--activerecord/test/cases/serialization_test.rb19
-rw-r--r--activerecord/test/cases/types_test.rb159
-rw-r--r--activerecord/test/fixtures/books.yml2
-rw-r--r--activerecord/test/schema/schema.rb7
-rw-r--r--activesupport/CHANGELOG.md14
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb12
-rw-r--r--activesupport/lib/active_support/dependencies.rb9
-rw-r--r--activesupport/lib/active_support/duration.rb3
-rw-r--r--activesupport/lib/active_support/test_case.rb2
-rw-r--r--activesupport/lib/active_support/time.rb2
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb4
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb6
-rw-r--r--activesupport/test/inflector_test.rb15
-rw-r--r--activesupport/test/subscriber_test.rb1
-rw-r--r--guides/CHANGELOG.md14
-rw-r--r--guides/code/getting_started/Gemfile4
-rw-r--r--guides/code/getting_started/Gemfile.lock85
-rw-r--r--guides/code/getting_started/app/assets/javascripts/articles.js.coffee (renamed from guides/code/getting_started/app/assets/javascripts/posts.js.coffee)0
-rw-r--r--guides/code/getting_started/app/assets/stylesheets/articles.css.scss (renamed from guides/code/getting_started/app/assets/stylesheets/posts.css.scss)2
-rw-r--r--guides/code/getting_started/app/controllers/articles_controller.rb53
-rw-r--r--guides/code/getting_started/app/controllers/comments_controller.rb12
-rw-r--r--guides/code/getting_started/app/controllers/posts_controller.rb53
-rw-r--r--guides/code/getting_started/app/helpers/articles_helper.rb2
-rw-r--r--guides/code/getting_started/app/helpers/posts_helper.rb2
-rw-r--r--guides/code/getting_started/app/models/article.rb (renamed from guides/code/getting_started/app/models/post.rb)4
-rw-r--r--guides/code/getting_started/app/models/comment.rb2
-rw-r--r--guides/code/getting_started/app/views/articles/_form.html.erb (renamed from guides/code/getting_started/app/views/posts/_form.html.erb)10
-rw-r--r--guides/code/getting_started/app/views/articles/edit.html.erb (renamed from guides/code/getting_started/app/views/posts/edit.html.erb)6
-rw-r--r--guides/code/getting_started/app/views/articles/index.html.erb21
-rw-r--r--guides/code/getting_started/app/views/articles/new.html.erb (renamed from guides/code/getting_started/app/views/posts/new.html.erb)6
-rw-r--r--guides/code/getting_started/app/views/articles/show.html.erb18
-rw-r--r--guides/code/getting_started/app/views/comments/_comment.html.erb4
-rw-r--r--guides/code/getting_started/app/views/comments/_form.html.erb2
-rw-r--r--guides/code/getting_started/app/views/posts/index.html.erb21
-rw-r--r--guides/code/getting_started/app/views/posts/show.html.erb18
-rw-r--r--guides/code/getting_started/app/views/welcome/index.html.erb4
-rw-r--r--guides/code/getting_started/config/routes.rb2
-rw-r--r--guides/code/getting_started/db/migrate/20130122042648_create_articles.rb (renamed from guides/code/getting_started/db/migrate/20130122042648_create_posts.rb)4
-rw-r--r--guides/code/getting_started/db/migrate/20130122045842_create_comments.rb2
-rw-r--r--guides/code/getting_started/db/schema.rb18
-rw-r--r--guides/code/getting_started/test/controllers/articles_controller_test.rb (renamed from guides/code/getting_started/test/controllers/posts_controller_test.rb)2
-rw-r--r--guides/code/getting_started/test/fixtures/articles.yml (renamed from guides/code/getting_started/test/fixtures/posts.yml)0
-rw-r--r--guides/code/getting_started/test/fixtures/comments.yml4
-rw-r--r--guides/code/getting_started/test/helpers/articles_helper_test.rb4
-rw-r--r--guides/code/getting_started/test/helpers/posts_helper_test.rb4
-rw-r--r--guides/code/getting_started/test/models/article_test.rb (renamed from guides/code/getting_started/test/models/post_test.rb)2
-rw-r--r--guides/code/getting_started/test/test_helper.rb3
-rw-r--r--guides/source/active_record_querying.md52
-rw-r--r--guides/source/command_line.md12
-rw-r--r--guides/source/configuring.md42
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md30
-rw-r--r--guides/source/debugging_rails_applications.md12
-rw-r--r--guides/source/getting_started.md2
-rw-r--r--guides/source/ruby_on_rails_guides_guidelines.md2
-rw-r--r--guides/source/testing.md5
-rw-r--r--railties/CHANGELOG.md7
-rw-r--r--railties/lib/rails/generators.rb28
-rw-r--r--railties/lib/rails/generators/actions.rb17
-rw-r--r--railties/lib/rails/generators/named_base.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/test_helper.rb3
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/Rakefile4
-rw-r--r--railties/lib/rails/info_controller.rb2
-rw-r--r--railties/lib/rails/mailers_controller.rb6
-rw-r--r--railties/lib/rails/ruby_version_check.rb2
-rw-r--r--railties/lib/rails/tasks/statistics.rake11
-rw-r--r--railties/test/generators/actions_test.rb22
-rw-r--r--railties/test/generators/argv_scrubber_test.rb2
-rw-r--r--railties/test/generators/generator_test.rb2
-rw-r--r--railties/test/generators_test.rb6
180 files changed, 1990 insertions, 1509 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 652ce0b3d8..5852aeaec2 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -301,12 +301,13 @@ module ActionMailer
# end
# end
#
- # Callbacks in ActionMailer are implemented using AbstractController::Callbacks, so you
- # can define and configure callbacks in the same manner that you would use callbacks in
- # classes that inherit from ActionController::Base.
+ # Callbacks in Action Mailer are implemented using
+ # <tt>AbstractController::Callbacks</tt>, so you can define and configure
+ # callbacks in the same manner that you would use callbacks in classes that
+ # inherit from <tt>ActionController::Base</tt>.
#
# Note that unless you have a specific reason to do so, you should prefer using before_action
- # rather than after_action in your ActionMailer classes so that headers are parsed properly.
+ # rather than after_action in your Action Mailer classes so that headers are parsed properly.
#
# = Previewing emails
#
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index 6452bf616c..06da0dd27e 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -1,4 +1,6 @@
module ActionMailer
+ # Provides helper methods for testing Action Mailer, including #assert_emails
+ # and #assert_no_emails
module TestHelper
# Asserts that the number of emails sent matches the given number.
#
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index a98aec913f..06f80a8fdc 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -1,7 +1,8 @@
require_relative 'gem_version'
module ActionMailer
- # Returns the version of the currently loaded ActionMailer as a <tt>Gem::Version</tt>
+ # Returns the version of the currently loaded Action Mailer as a
+ # <tt>Gem::Version</tt>.
def self.version
gem_version
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 2b4ff7f03d..7da58ca4e7 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,9 +1,18 @@
+* Deprecate all *_filter callbacks in favor of *_action callbacks.
+
+ *Rafael Mendonça França*
+
+* Fix URL generation with `:trailing_slash` such that it does not add
+ a trailing slash after `.:format`
+
+ *Dan Langevin*
+
* Build full URI as string when processing path in integration tests for
performance reasons.
*Guo Xiang Tan*
-* Fix 'Stack level too deep' when rendering `head :ok` in an action method
+* Fix `'Stack level too deep'` when rendering `head :ok` in an action method
called 'status' in a controller.
Fixes #13905.
@@ -71,7 +80,7 @@
4. Use `escape_segment` rather than `escape_path` in URL generation
For point 4 there are two exceptions. Firstly, when a route uses wildcard segments
- (e.g. *foo) then we use `escape_path` as the value may contain '/' characters. This
+ (e.g. `*foo`) then we use `escape_path` as the value may contain '/' characters. This
means that wildcard routes can't be optimized. Secondly, if a `:controller` segment
is used in the path then this uses `escape_path` as the controller may be namespaced.
@@ -101,7 +110,7 @@
*Andrew White*, *James Coglan*
-* Append link to bad code to backtrace when exception is SyntaxError.
+* Append link to bad code to backtrace when exception is `SyntaxError`.
*Boris Kuznetsov*
@@ -131,4 +140,5 @@
*Tony Wooster*
+
Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/actionpack/CHANGELOG.md) for previous changes.
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index c00f0d0c6f..acdfb33efa 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -255,7 +255,7 @@ module AbstractController
# Checks if the action name is valid and returns false otherwise.
def _valid_action_name?(action_name)
- action_name.to_s !~ Regexp.new(File::SEPARATOR)
+ action_name !~ Regexp.new(File::SEPARATOR)
end
end
end
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 69aca308d6..252e297c69 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation'
+
module AbstractController
module Callbacks
extend ActiveSupport::Concern
@@ -42,21 +44,23 @@ module AbstractController
end
end
- # Skip before, after, and around action callbacks matching any of the names
- # Aliased as skip_filter.
+ # Skip before, after, and around action callbacks matching any of the names.
#
# ==== Parameters
# * <tt>names</tt> - A list of valid names that could be used for
# callbacks. Note that skipping uses Ruby equality, so it's
# impossible to skip a callback defined using an anonymous proc
- # using #skip_filter
+ # using #skip_action_callback
def skip_action_callback(*names)
skip_before_action(*names)
skip_after_action(*names)
skip_around_action(*names)
end
- alias_method :skip_filter, :skip_action_callback
+ def skip_filter(*names)
+ ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will removed in Rails 5. Use #{callback}_action instead.")
+ skip_action_callback(*names)
+ end
# Take callback names and an optional callback proc, normalize them,
# then call the block with each callback. This allows us to abstract
@@ -85,7 +89,6 @@ module AbstractController
# :call-seq: before_action(names, block)
#
# Append a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as before_filter.
##
# :method: prepend_before_action
@@ -93,7 +96,6 @@ module AbstractController
# :call-seq: prepend_before_action(names, block)
#
# Prepend a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as prepend_before_filter.
##
# :method: skip_before_action
@@ -101,7 +103,6 @@ module AbstractController
# :call-seq: skip_before_action(names)
#
# Skip a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as skip_before_filter.
##
# :method: append_before_action
@@ -109,7 +110,6 @@ module AbstractController
# :call-seq: append_before_action(names, block)
#
# Append a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as append_before_filter.
##
# :method: after_action
@@ -117,7 +117,6 @@ module AbstractController
# :call-seq: after_action(names, block)
#
# Append a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as after_filter.
##
# :method: prepend_after_action
@@ -125,7 +124,6 @@ module AbstractController
# :call-seq: prepend_after_action(names, block)
#
# Prepend a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as prepend_after_filter.
##
# :method: skip_after_action
@@ -133,7 +131,6 @@ module AbstractController
# :call-seq: skip_after_action(names)
#
# Skip a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as skip_after_filter.
##
# :method: append_after_action
@@ -141,7 +138,6 @@ module AbstractController
# :call-seq: append_after_action(names, block)
#
# Append a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as append_after_filter.
##
# :method: around_action
@@ -149,7 +145,6 @@ module AbstractController
# :call-seq: around_action(names, block)
#
# Append a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as around_filter.
##
# :method: prepend_around_action
@@ -157,7 +152,6 @@ module AbstractController
# :call-seq: prepend_around_action(names, block)
#
# Prepend a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as prepend_around_filter.
##
# :method: skip_around_action
@@ -165,7 +159,6 @@ module AbstractController
# :call-seq: skip_around_action(names)
#
# Skip a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as skip_around_filter.
##
# :method: append_around_action
@@ -173,7 +166,6 @@ module AbstractController
# :call-seq: append_around_action(names, block)
#
# Append a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as append_around_filter.
# set up before_action, prepend_before_action, skip_before_action, etc.
# for each of before, after, and around.
@@ -184,7 +176,10 @@ module AbstractController
end
end
- alias_method :"#{callback}_filter", :"#{callback}_action"
+ define_method "#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will removed in Rails 5. Use #{callback}_action instead.")
+ send("#{callback}_action", *names, &blk)
+ end
define_method "prepend_#{callback}_action" do |*names, &blk|
_insert_callbacks(names, blk) do |name, options|
@@ -192,7 +187,10 @@ module AbstractController
end
end
- alias_method :"prepend_#{callback}_filter", :"prepend_#{callback}_action"
+ define_method "prepend_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("prepend_#{callback}_filter is deprecated and will removed in Rails 5. Use prepend_#{callback}_action instead.")
+ send("prepend_#{callback}_action", *names, &blk)
+ end
# Skip a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
@@ -202,11 +200,17 @@ module AbstractController
end
end
- alias_method :"skip_#{callback}_filter", :"skip_#{callback}_action"
+ define_method "skip_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("skip_#{callback}_filter is deprecated and will removed in Rails 5. Use skip_#{callback}_action instead.")
+ send("skip_#{callback}_action", *names, &blk)
+ end
# *_action is the same as append_*_action
alias_method :"append_#{callback}_action", :"#{callback}_action" # alias_method :append_before_action, :before_action
- alias_method :"append_#{callback}_filter", :"#{callback}_action" # alias_method :append_before_filter, :before_action
+ define_method "append_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("append_#{callback}_filter is deprecated and will removed in Rails 5. Use append_#{callback}_action instead.")
+ send("append_#{callback}_action", *names, &blk)
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 696fbf6e09..9a427ebfdb 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -30,10 +30,8 @@ module ActionController
end
end
- def build(action, app=nil, &block)
- app ||= block
+ def build(action, app = Proc.new)
action = action.to_s
- raise "MiddlewareStack#build requires an app" unless app
middlewares.reverse.inject(app) do |a, middleware|
middleware.valid?(action) ? middleware.build(a) : a
@@ -223,13 +221,18 @@ module ActionController
# Makes the controller a Rack endpoint that runs the action in the given
# +env+'s +action_dispatch.request.path_parameters+ key.
def self.call(env)
- action(env['action_dispatch.request.path_parameters'][:action]).call(env)
+ req = ActionDispatch::Request.new env
+ action(req.path_parameters[:action]).call(env)
end
# Returns a Rack endpoint for the given action name.
def self.action(name, klass = ActionDispatch::Request)
- middleware_stack.build(name.to_s) do |env|
- new.dispatch(name, klass.new(env))
+ if middleware_stack.any?
+ middleware_stack.build(name) do |env|
+ new.dispatch(name, klass.new(env))
+ end
+ else
+ lambda { |env| new.dispatch(name, klass.new(env)) }
end
end
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 29ce5abd55..46405cef55 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -78,7 +78,7 @@ module ActionController
# respond_to do |format|
# format.html
# format.csv { render csv: @csvable, filename: @csvable.name }
- # }
+ # end
# end
# To use renderers and their mime types in more concise ways, see
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 5b22cd1fcd..1ab11392ce 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -4,10 +4,7 @@ require 'active_support/core_ext/hash/indifferent_access'
module ActionDispatch
module Http
module Parameters
- def initialize(env)
- super
- @symbolized_path_params = nil
- end
+ PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
# Returns both GET and POST \parameters in a single hash.
def parameters
@@ -25,7 +22,7 @@ module ActionDispatch
def path_parameters=(parameters) #:nodoc:
@env.delete('action_dispatch.request.parameters')
- @env[Routing::RouteSet::PARAMETERS_KEY] = parameters
+ @env[PARAMETERS_KEY] = parameters
end
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
@@ -40,11 +37,7 @@ module ActionDispatch
#
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
def path_parameters
- @env[Routing::RouteSet::PARAMETERS_KEY] ||= {}
- end
-
- def reset_parameters #:nodoc:
- @env.delete("action_dispatch.request.parameters")
+ @env[PARAMETERS_KEY] ||= {}
end
private
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index cdb3e44b3a..dfe258e463 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -53,6 +53,17 @@ module ActionDispatch
@uuid = nil
end
+ def check_path_parameters!
+ # If any of the path parameters has an invalid encoding then
+ # raise since it's likely to trigger errors further on.
+ path_parameters.each do |key, value|
+ next unless value.respond_to?(:valid_encoding?)
+ unless value.valid_encoding?
+ raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
+ end
+ end
+ end
+
def key?(key)
@env.key?(key)
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index a6c17f50a5..4cba4f5f37 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -37,13 +37,7 @@ module ActionDispatch
path = options[:script_name].to_s.chomp("/")
path << options[:path].to_s
- if options[:trailing_slash]
- if path.include?('?')
- path.sub!(/\?/, '/\&')
- else
- path.sub!(/[^\/]\z|\A\z/, '\&/')
- end
- end
+ add_trailing_slash(path) if options[:trailing_slash]
result = path
@@ -66,6 +60,18 @@ module ActionDispatch
private
+ def add_trailing_slash(path)
+ # includes querysting
+ if path.include?('?')
+ path.sub!(/\?/, '/\&')
+ # does not have a .format
+ elsif !path.include?(".")
+ path.sub!(/[^\/]\z|\A\z/, '\&/')
+ end
+
+ path
+ end
+
def build_host_url(options)
if match = options[:host].match(HOST_REGEXP)
options[:protocol] ||= match[1] unless options[:protocol] == false
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index 8b3081d8fe..6d58323789 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -132,11 +132,6 @@ module ActionDispatch
}
end
- # Returns +true+ if no missing keys are present, otherwise +false+.
- def verify_required_parts!(route, parts)
- missing_keys(route, parts).empty?
- end
-
def build_cache
root = { ___routes: [] }
routes.each_with_index do |route, i|
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index cc3c7f20cb..9f0a3af902 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -16,14 +16,6 @@ module ActionDispatch
@app = app
@path = path
- # Unwrap any constraints so we can see what's inside for route generation.
- # This allows the formatter to skip over any mounted applications or redirects
- # that shouldn't be matched when using a url_for without a route name.
- while app.is_a?(Routing::Mapper::Constraints) do
- app = app.app
- end
- @dispatcher = app.is_a?(Routing::RouteSet::Dispatcher)
-
@constraints = constraints
@defaults = defaults
@required_defaults = nil
@@ -99,7 +91,7 @@ module ActionDispatch
end
def dispatcher?
- @dispatcher
+ @app.dispatcher?
end
def matches?(request)
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index c34b44409e..74fa9ee3a2 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -20,61 +20,30 @@ module ActionDispatch
# :nodoc:
VERSION = '2.0.0'
- class NullReq # :nodoc:
- attr_reader :env
- attr_accessor :path_parameters
- def initialize(env)
- @env = env
- @path_parameters = {}
- end
-
- def request_method
- env['REQUEST_METHOD']
- end
-
- def path_info
- env['PATH_INFO']
- end
-
- def ip
- env['REMOTE_ADDR']
- end
-
- def [](k)
- env[k]
- end
- end
-
- attr_reader :request_class, :formatter
attr_accessor :routes
- def initialize(routes, options)
- @options = options
- @request_class = options[:request_class] || NullReq
- @routes = routes
+ def initialize(routes)
+ @routes = routes
end
- def call(env)
- env['PATH_INFO'] = Utils.normalize_path(env['PATH_INFO'])
-
- req = request_class.new(env)
-
- find_routes(env, req).each do |match, parameters, route|
- set_params = req.path_parameters
- script_name, path_info = env.values_at('SCRIPT_NAME', 'PATH_INFO')
+ def serve(req)
+ find_routes(req).each do |match, parameters, route|
+ set_params = req.path_parameters
+ path_info = req.path_info
+ script_name = req.script_name
unless route.path.anchored
- env['SCRIPT_NAME'] = (script_name.to_s + match.to_s).chomp('/')
- env['PATH_INFO'] = match.post_match
+ req.script_name = (script_name.to_s + match.to_s).chomp('/')
+ req.path_info = match.post_match
end
req.path_parameters = set_params.merge parameters
- status, headers, body = route.app.call(env)
+ status, headers, body = route.app.serve(req)
if 'pass' == headers['X-Cascade']
- env['SCRIPT_NAME'] = script_name
- env['PATH_INFO'] = path_info
+ req.script_name = script_name
+ req.path_info = path_info
req.path_parameters = set_params
next
end
@@ -85,13 +54,11 @@ module ActionDispatch
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
end
- def recognize(req)
- rails_req = request_class.new(req.env)
-
- find_routes(req.env, rails_req).each do |match, parameters, route|
+ def recognize(rails_req)
+ find_routes(rails_req).each do |match, parameters, route|
unless route.path.anchored
- req.env['SCRIPT_NAME'] = match.to_s
- req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
+ rails_req.script_name = match.to_s
+ rails_req.path_info = match.post_match.sub(/^([^\/])/, '/\1')
end
yield(route, parameters)
@@ -128,11 +95,14 @@ module ActionDispatch
simulator.memos(path) { [] }
end
- def find_routes env, req
+ def find_routes req
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
r.path.match(req.path_info)
}
- routes.concat get_routes_as_head(routes)
+
+ if req.env["REQUEST_METHOD"] === "HEAD"
+ routes.concat get_routes_as_head(routes)
+ end
routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
@@ -148,7 +118,7 @@ module ActionDispatch
def get_routes_as_head(routes)
precedence = (routes.map(&:precedence).max || 0) + 1
- routes = routes.select { |r|
+ routes.select { |r|
r.verb === "GET" && !(r.verb === "HEAD")
}.map! { |r|
Route.new(r.name,
@@ -159,8 +129,6 @@ module ActionDispatch
route.precedence = r.precedence + precedence
end
}
- routes.flatten!
- routes
end
end
end
diff --git a/actionpack/lib/action_dispatch/journey/visualizer/index.html.erb b/actionpack/lib/action_dispatch/journey/visualizer/index.html.erb
index 6aff10956a..9b28a65200 100644
--- a/actionpack/lib/action_dispatch/journey/visualizer/index.html.erb
+++ b/actionpack/lib/action_dispatch/journey/visualizer/index.html.erb
@@ -2,13 +2,13 @@
<html>
<head>
<title><%= title %></title>
- <link rel="stylesheet" href="https://raw.github.com/gist/1706081/af944401f75ea20515a02ddb3fb43d23ecb8c662/reset.css" type="text/css">
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.css" type="text/css">
<style>
<% stylesheets.each do |style| %>
<%= style %>
<% end %>
</style>
- <script src="https://raw.github.com/gist/1706081/df464722a05c3c2bec450b7b5c8240d9c31fa52d/d3.min.js" type="text/javascript"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js" type="text/javascript"></script>
</head>
<body>
<div id="wrapper">
diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
index cce0d75af4..6ffa242da4 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
@@ -148,8 +148,8 @@
// On key press perform a search for matching paths
searchElem.onkeyup = function(e){
var userInput = searchElem.value,
- defaultExactMatch = '<tr><th colspan="4">Paths Matching (' + sanitizePath(userInput) +'):</th></tr>',
- defaultFuzzyMatch = '<tr><th colspan="4">Paths Containing (' + userInput +'):</th></tr>',
+ defaultExactMatch = '<tr><th colspan="4">Paths Matching (' + escape(sanitizePath(userInput)) +'):</th></tr>',
+ defaultFuzzyMatch = '<tr><th colspan="4">Paths Containing (' + escape(userInput) +'):</th></tr>',
noExactMatch = '<tr><th colspan="4">No Exact Matches Found</th></tr>',
noFuzzyMatch = '<tr><th colspan="4">No Fuzzy Matches Found</th></tr>';
diff --git a/actionpack/lib/action_dispatch/routing/endpoint.rb b/actionpack/lib/action_dispatch/routing/endpoint.rb
new file mode 100644
index 0000000000..88aa13c3e8
--- /dev/null
+++ b/actionpack/lib/action_dispatch/routing/endpoint.rb
@@ -0,0 +1,10 @@
+module ActionDispatch
+ module Routing
+ class Endpoint # :nodoc:
+ def dispatcher?; false; end
+ def redirect?; false; end
+ def matches?(req); true; end
+ def app; self; end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index 71a0c5e826..ea3b2f419d 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -5,22 +5,15 @@ module ActionDispatch
module Routing
class RouteWrapper < SimpleDelegator
def endpoint
- rack_app ? rack_app.inspect : "#{controller}##{action}"
+ app.dispatcher? ? "#{controller}##{action}" : rack_app.inspect
end
def constraints
requirements.except(:controller, :action)
end
- def rack_app(app = self.app)
- @rack_app ||= begin
- class_name = app.class.name.to_s
- if class_name == "ActionDispatch::Routing::Mapper::Constraints"
- rack_app(app.app)
- elsif ActionDispatch::Routing::Redirect === app || class_name !~ /^ActionDispatch::Routing/
- app
- end
- end
+ def rack_app
+ app.app
end
def verb
@@ -73,7 +66,7 @@ module ActionDispatch
end
def engine?
- rack_app && rack_app.respond_to?(:routes)
+ rack_app.respond_to?(:routes)
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 7e78b417fa..84a08770f5 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -6,6 +6,7 @@ require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/module/remove_method'
require 'active_support/inflector'
require 'action_dispatch/routing/redirection'
+require 'action_dispatch/routing/endpoint'
module ActionDispatch
module Routing
@@ -15,34 +16,41 @@ module ActionDispatch
:controller, :action, :path_names, :constraints,
:shallow, :blocks, :defaults, :options]
- class Constraints #:nodoc:
- def self.new(app, constraints, request = Rack::Request)
- if constraints.any?
- super(app, constraints, request)
- else
- app
+ class Constraints < Endpoint #:nodoc:
+ attr_reader :app, :constraints
+
+ def initialize(app, constraints, dispatcher_p)
+ # Unwrap Constraints objects. I don't actually think it's possible
+ # to pass a Constraints object to this constructor, but there were
+ # multiple places that kept testing children of this object. I
+ # *think* they were just being defensive, but I have no idea.
+ if app.is_a?(self.class)
+ constraints += app.constraints
+ app = app.app
end
- end
- attr_reader :app, :constraints
+ @dispatcher = dispatcher_p
- def initialize(app, constraints, request)
- @app, @constraints, @request = app, constraints, request
+ @app, @constraints, = app, constraints
end
- def matches?(env)
- req = @request.new(env)
+ def dispatcher?; @dispatcher; end
+ def matches?(req)
@constraints.all? do |constraint|
(constraint.respond_to?(:matches?) && constraint.matches?(req)) ||
(constraint.respond_to?(:call) && constraint.call(*constraint_args(constraint, req)))
end
- ensure
- req.reset_parameters
end
- def call(env)
- matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
+ def serve(req)
+ return [ 404, {'X-Cascade' => 'pass'}, [] ] unless matches?(req)
+
+ if dispatcher?
+ @app.serve req
+ else
+ @app.call req.env
+ end
end
private
@@ -57,12 +65,18 @@ module ActionDispatch
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
attr_reader :scope, :path, :options, :requirements, :conditions, :defaults
+ attr_reader :to, :default_controller, :default_action
def initialize(set, scope, path, options)
- @set, @scope, @path, @options = set, scope, path, options
+ @set, @scope, @path = set, scope, path
@requirements, @conditions, @defaults = {}, {}, {}
- normalize_options!
+ options = scope[:options].merge(options) if scope[:options]
+ @to = options[:to]
+ @default_controller = options[:controller] || scope[:controller]
+ @default_action = options[:action] || scope[:action]
+
+ @options = normalize_options!(options)
normalize_path!
normalize_requirements!
normalize_conditions!
@@ -94,14 +108,13 @@ module ActionDispatch
options[:format] != false && !path.include?(':format') && !path.end_with?('/')
end
- def normalize_options!
- @options.reverse_merge!(scope[:options]) if scope[:options]
+ def normalize_options!(options)
path_without_format = path.sub(/\(\.:format\)$/, '')
# Add a constraint for wildcard route to make it non-greedy and match the
# optional format part of the route by default
- if path_without_format.match(WILDCARD_PATH) && @options[:format] != false
- @options[$1.to_sym] ||= /.+?/
+ if path_without_format.match(WILDCARD_PATH) && options[:format] != false
+ options[$1.to_sym] ||= /.+?/
end
if path_without_format.match(':controller')
@@ -111,10 +124,10 @@ module ActionDispatch
# controllers with default routes like :controller/:action/:id(.:format), e.g:
# GET /admin/products/show/1
# => { controller: 'admin/products', action: 'show', id: '1' }
- @options[:controller] ||= /.+?/
+ options[:controller] ||= /.+?/
end
- @options.merge!(default_controller_and_action)
+ options.merge!(default_controller_and_action)
end
def normalize_requirements!
@@ -210,7 +223,17 @@ module ActionDispatch
end
def app
- Constraints.new(endpoint, blocks, @set.request_class)
+ return to if Redirect === to
+
+ if to.respond_to?(:call)
+ Constraints.new(to, blocks, false)
+ else
+ if blocks.any?
+ Constraints.new(dispatcher, blocks, true)
+ else
+ dispatcher
+ end
+ end
end
def default_controller_and_action
@@ -296,24 +319,8 @@ module ActionDispatch
Journey::Router::Strexp.compile(path, requirements, SEPARATORS)
end
- def endpoint
- to.respond_to?(:call) ? to : dispatcher
- end
-
def dispatcher
- Routing::RouteSet::Dispatcher.new(:defaults => defaults)
- end
-
- def to
- options[:to]
- end
-
- def default_controller
- options[:controller] || scope[:controller]
- end
-
- def default_action
- options[:action] || scope[:action]
+ Routing::RouteSet::Dispatcher.new(defaults)
end
end
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index f8ed0cbe6a..b1b39a5496 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -3,10 +3,11 @@ require 'active_support/core_ext/uri'
require 'active_support/core_ext/array/extract_options'
require 'rack/utils'
require 'action_controller/metal/exceptions'
+require 'action_dispatch/routing/endpoint'
module ActionDispatch
module Routing
- class Redirect # :nodoc:
+ class Redirect < Endpoint # :nodoc:
attr_reader :status, :block
def initialize(status, block)
@@ -14,17 +15,14 @@ module ActionDispatch
@block = block
end
- def call(env)
- req = Request.new(env)
+ def redirect?; true; end
- # If any of the path parameters has an invalid encoding then
- # raise since it's likely to trigger errors further on.
- req.path_parameters.each do |key, value|
- unless value.valid_encoding?
- raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
- end
- end
+ def call(env)
+ serve Request.new env
+ end
+ def serve(req)
+ req.check_path_parameters!
uri = URI.parse(path(req.path_parameters, req))
unless uri.host
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 8b4fd26ce2..3ca5abf0fd 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -8,6 +8,7 @@ require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/array/extract_options'
require 'action_controller/metal/exceptions'
require 'action_dispatch/http/request'
+require 'action_dispatch/routing/endpoint'
module ActionDispatch
module Routing
@@ -18,27 +19,17 @@ module ActionDispatch
# alias inspect to to_s.
alias inspect to_s
- PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
-
- class Dispatcher #:nodoc:
- def initialize(options={})
- @defaults = options[:defaults]
- @glob_param = options.delete(:glob)
+ class Dispatcher < Routing::Endpoint #:nodoc:
+ def initialize(defaults)
+ @defaults = defaults
@controller_class_names = ThreadSafe::Cache.new
end
- def call(env)
- params = env[PARAMETERS_KEY]
-
- # If any of the path parameters has an invalid encoding then
- # raise since it's likely to trigger errors further on.
- params.each do |key, value|
- next unless value.respond_to?(:valid_encoding?)
+ def dispatcher?; true; end
- unless value.valid_encoding?
- raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
- end
- end
+ def serve(req)
+ req.check_path_parameters!
+ params = req.path_parameters
prepare_params!(params)
@@ -47,13 +38,12 @@ module ActionDispatch
return [404, {'X-Cascade' => 'pass'}, []]
end
- dispatch(controller, params[:action], env)
+ dispatch(controller, params[:action], req.env)
end
def prepare_params!(params)
normalize_controller!(params)
merge_default_action!(params)
- split_glob_param!(params) if @glob_param
end
# If this is a default_controller (i.e. a controller specified by the user)
@@ -89,10 +79,6 @@ module ActionDispatch
def merge_default_action!(params)
params[:action] ||= 'index'
end
-
- def split_glob_param!(params)
- params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
- end
end
# A NamedRouteCollection instance is a collection of named routes, and also
@@ -302,8 +288,7 @@ module ActionDispatch
@finalized = false
@set = Journey::Routes.new
- @router = Journey::Router.new(@set, {
- :request_class => request_class})
+ @router = Journey::Router.new @set
@formatter = Journey::Formatter.new @set
end
@@ -683,7 +668,9 @@ module ActionDispatch
end
def call(env)
- @router.call(env)
+ req = request_class.new(env)
+ req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
+ @router.serve(req)
end
def recognize_path(path, environment = {})
@@ -697,7 +684,7 @@ module ActionDispatch
raise ActionController::RoutingError, e.message
end
- req = @request_class.new(env)
+ req = request_class.new(env)
@router.recognize(req) do |route, params|
params.merge!(extras)
params.each do |key, value|
@@ -708,12 +695,10 @@ module ActionDispatch
end
old_params = req.path_parameters
req.path_parameters = old_params.merge params
- dispatcher = route.app
- while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
- dispatcher = dispatcher.app
- end
+ app = route.app
+ if app.matches?(req) && app.dispatcher?
+ dispatcher = app.app
- if dispatcher.is_a?(Dispatcher)
if dispatcher.controller(params, false)
dispatcher.prepare_params!(params)
return params
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index d900f3c7a9..107e62d20f 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -3,7 +3,6 @@ require 'uri'
require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/object/try'
require 'rack/test'
-require 'minitest'
module ActionDispatch
module Integration #:nodoc:
diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb
index 8cba049485..07571602e4 100644
--- a/actionpack/test/abstract/callbacks_test.rb
+++ b/actionpack/test/abstract/callbacks_test.rb
@@ -267,9 +267,11 @@ module AbstractController
end
class AliasedCallbacks < ControllerWithCallbacks
- before_filter :first
- after_filter :second
- around_filter :aroundz
+ ActiveSupport::Deprecation.silence do
+ before_filter :first
+ after_filter :second
+ around_filter :aroundz
+ end
def first
@text = "Hello world"
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index c87494aa64..b2b01b3fa9 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -2,13 +2,13 @@ require 'abstract_unit'
class ActionController::Base
class << self
- %w(append_around_filter prepend_after_filter prepend_around_filter prepend_before_filter skip_after_filter skip_before_filter skip_filter).each do |pending|
+ %w(append_around_action prepend_after_action prepend_around_action prepend_before_action skip_after_action skip_before_action skip_action_callback).each do |pending|
define_method(pending) do |*args|
$stderr.puts "#{pending} unimplemented: #{args.inspect}"
end unless method_defined?(pending)
end
- def before_filters
+ def before_actions
filters = _process_action_callbacks.select { |c| c.kind == :before }
filters.map! { |c| c.raw_filter }
end
@@ -28,8 +28,8 @@ end
class FilterTest < ActionController::TestCase
class TestController < ActionController::Base
- before_filter :ensure_login
- after_filter :clean_up
+ before_action :ensure_login
+ after_action :clean_up
def show
render :inline => "ran action"
@@ -42,13 +42,13 @@ class FilterTest < ActionController::TestCase
end
def clean_up
- @ran_after_filter ||= []
- @ran_after_filter << "clean_up"
+ @ran_after_action ||= []
+ @ran_after_action << "clean_up"
end
end
class ChangingTheRequirementsController < TestController
- before_filter :ensure_login, :except => [:go_wild]
+ before_action :ensure_login, :except => [:go_wild]
def go_wild
render :text => "gobble"
@@ -56,9 +56,9 @@ class FilterTest < ActionController::TestCase
end
class TestMultipleFiltersController < ActionController::Base
- before_filter :try_1
- before_filter :try_2
- before_filter :try_3
+ before_action :try_1
+ before_action :try_2
+ before_action :try_3
(1..3).each do |i|
define_method "fail_#{i}" do
@@ -78,8 +78,8 @@ class FilterTest < ActionController::TestCase
end
class RenderingController < ActionController::Base
- before_filter :before_filter_rendering
- after_filter :unreached_after_filter
+ before_action :before_action_rendering
+ after_action :unreached_after_action
def show
@ran_action = true
@@ -87,29 +87,29 @@ class FilterTest < ActionController::TestCase
end
private
- def before_filter_rendering
+ def before_action_rendering
@ran_filter ||= []
- @ran_filter << "before_filter_rendering"
+ @ran_filter << "before_action_rendering"
render :inline => "something else"
end
- def unreached_after_filter
- @ran_filter << "unreached_after_filter_after_render"
+ def unreached_after_action
+ @ran_filter << "unreached_after_action_after_render"
end
end
- class RenderingForPrependAfterFilterController < RenderingController
- prepend_after_filter :unreached_prepend_after_filter
+ class RenderingForPrependAfterActionController < RenderingController
+ prepend_after_action :unreached_prepend_after_action
private
- def unreached_prepend_after_filter
- @ran_filter << "unreached_preprend_after_filter_after_render"
+ def unreached_prepend_after_action
+ @ran_filter << "unreached_preprend_after_action_after_render"
end
end
- class BeforeFilterRedirectionController < ActionController::Base
- before_filter :before_filter_redirects
- after_filter :unreached_after_filter
+ class BeforeActionRedirectionController < ActionController::Base
+ before_action :before_action_redirects
+ after_action :unreached_after_action
def show
@ran_action = true
@@ -122,23 +122,23 @@ class FilterTest < ActionController::TestCase
end
private
- def before_filter_redirects
+ def before_action_redirects
@ran_filter ||= []
- @ran_filter << "before_filter_redirects"
+ @ran_filter << "before_action_redirects"
redirect_to(:action => 'target_of_redirection')
end
- def unreached_after_filter
- @ran_filter << "unreached_after_filter_after_redirection"
+ def unreached_after_action
+ @ran_filter << "unreached_after_action_after_redirection"
end
end
- class BeforeFilterRedirectionForPrependAfterFilterController < BeforeFilterRedirectionController
- prepend_after_filter :unreached_prepend_after_filter_after_redirection
+ class BeforeActionRedirectionForPrependAfterActionController < BeforeActionRedirectionController
+ prepend_after_action :unreached_prepend_after_action_after_redirection
private
- def unreached_prepend_after_filter_after_redirection
- @ran_filter << "unreached_prepend_after_filter_after_redirection"
+ def unreached_prepend_after_action_after_redirection
+ @ran_filter << "unreached_prepend_after_action_after_redirection"
end
end
@@ -151,8 +151,8 @@ class FilterTest < ActionController::TestCase
render :inline => "ran action"
end
- def show_without_filter
- render :inline => "ran action without filter"
+ def show_without_action
+ render :inline => "ran action without action"
end
private
@@ -168,70 +168,70 @@ class FilterTest < ActionController::TestCase
end
class ConditionalCollectionFilterController < ConditionalFilterController
- before_filter :ensure_login, :except => [ :show_without_filter, :another_action ]
+ before_action :ensure_login, :except => [ :show_without_action, :another_action ]
end
class OnlyConditionSymController < ConditionalFilterController
- before_filter :ensure_login, :only => :show
+ before_action :ensure_login, :only => :show
end
class ExceptConditionSymController < ConditionalFilterController
- before_filter :ensure_login, :except => :show_without_filter
+ before_action :ensure_login, :except => :show_without_action
end
class BeforeAndAfterConditionController < ConditionalFilterController
- before_filter :ensure_login, :only => :show
- after_filter :clean_up_tmp, :only => :show
+ before_action :ensure_login, :only => :show
+ after_action :clean_up_tmp, :only => :show
end
class OnlyConditionProcController < ConditionalFilterController
- before_filter(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
+ before_action(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_action", true) }
end
class ExceptConditionProcController < ConditionalFilterController
- before_filter(:except => :show_without_filter) {|c| c.instance_variable_set(:"@ran_proc_filter", true) }
+ before_action(:except => :show_without_action) {|c| c.instance_variable_set(:"@ran_proc_action", true) }
end
class ConditionalClassFilter
- def self.before(controller) controller.instance_variable_set(:"@ran_class_filter", true) end
+ def self.before(controller) controller.instance_variable_set(:"@ran_class_action", true) end
end
class OnlyConditionClassController < ConditionalFilterController
- before_filter ConditionalClassFilter, :only => :show
+ before_action ConditionalClassFilter, :only => :show
end
class ExceptConditionClassController < ConditionalFilterController
- before_filter ConditionalClassFilter, :except => :show_without_filter
+ before_action ConditionalClassFilter, :except => :show_without_action
end
class AnomolousYetValidConditionController < ConditionalFilterController
- before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_filter1", true)}, :except => :show_without_filter) { |c| c.instance_variable_set(:"@ran_proc_filter2", true)}
+ before_action(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_action1", true)}, :except => :show_without_action) { |c| c.instance_variable_set(:"@ran_proc_action2", true)}
end
class OnlyConditionalOptionsFilter < ConditionalFilterController
- before_filter :ensure_login, :only => :index, :if => Proc.new {|c| c.instance_variable_set(:"@ran_conditional_index_proc", true) }
+ before_action :ensure_login, :only => :index, :if => Proc.new {|c| c.instance_variable_set(:"@ran_conditional_index_proc", true) }
end
class ConditionalOptionsFilter < ConditionalFilterController
- before_filter :ensure_login, :if => Proc.new { |c| true }
- before_filter :clean_up_tmp, :if => Proc.new { |c| false }
+ before_action :ensure_login, :if => Proc.new { |c| true }
+ before_action :clean_up_tmp, :if => Proc.new { |c| false }
end
class ConditionalOptionsSkipFilter < ConditionalFilterController
- before_filter :ensure_login
- before_filter :clean_up_tmp
+ before_action :ensure_login
+ before_action :clean_up_tmp
- skip_before_filter :ensure_login, if: -> { false }
- skip_before_filter :clean_up_tmp, if: -> { true }
+ skip_before_action :ensure_login, if: -> { false }
+ skip_before_action :clean_up_tmp, if: -> { true }
end
class ClassController < ConditionalFilterController
- before_filter ConditionalClassFilter
+ before_action ConditionalClassFilter
end
class PrependingController < TestController
- prepend_before_filter :wonderful_life
- # skip_before_filter :fire_flash
+ prepend_before_action :wonderful_life
+ # skip_before_action :fire_flash
private
def wonderful_life
@@ -241,8 +241,8 @@ class FilterTest < ActionController::TestCase
end
class SkippingAndLimitedController < TestController
- skip_before_filter :ensure_login
- before_filter :ensure_login, :only => :index
+ skip_before_action :ensure_login
+ before_action :ensure_login, :only => :index
def index
render :text => 'ok'
@@ -254,9 +254,9 @@ class FilterTest < ActionController::TestCase
end
class SkippingAndReorderingController < TestController
- skip_before_filter :ensure_login
- before_filter :find_record
- before_filter :ensure_login
+ skip_before_action :ensure_login
+ before_action :find_record
+ before_action :ensure_login
def index
render :text => 'ok'
@@ -270,10 +270,10 @@ class FilterTest < ActionController::TestCase
end
class ConditionalSkippingController < TestController
- skip_before_filter :ensure_login, :only => [ :login ]
- skip_after_filter :clean_up, :only => [ :login ]
+ skip_before_action :ensure_login, :only => [ :login ]
+ skip_after_action :clean_up, :only => [ :login ]
- before_filter :find_user, :only => [ :change_password ]
+ before_action :find_user, :only => [ :change_password ]
def login
render :inline => "ran action"
@@ -291,8 +291,8 @@ class FilterTest < ActionController::TestCase
end
class ConditionalParentOfConditionalSkippingController < ConditionalFilterController
- before_filter :conditional_in_parent_before, :only => [:show, :another_action]
- after_filter :conditional_in_parent_after, :only => [:show, :another_action]
+ before_action :conditional_in_parent_before, :only => [:show, :another_action]
+ after_action :conditional_in_parent_after, :only => [:show, :another_action]
private
@@ -308,20 +308,20 @@ class FilterTest < ActionController::TestCase
end
class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
- skip_before_filter :conditional_in_parent_before, :only => :another_action
- skip_after_filter :conditional_in_parent_after, :only => :another_action
+ skip_before_action :conditional_in_parent_before, :only => :another_action
+ skip_after_action :conditional_in_parent_after, :only => :another_action
end
class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
- skip_before_filter :conditional_in_parent_before, :only => :show
+ skip_before_action :conditional_in_parent_before, :only => :show
end
class ProcController < PrependingController
- before_filter(proc { |c| c.instance_variable_set(:"@ran_proc_filter", true) })
+ before_action(proc { |c| c.instance_variable_set(:"@ran_proc_action", true) })
end
class ImplicitProcController < PrependingController
- before_filter { |c| c.instance_variable_set(:"@ran_proc_filter", true) }
+ before_action { |c| c.instance_variable_set(:"@ran_proc_action", true) }
end
class AuditFilter
@@ -367,7 +367,7 @@ class FilterTest < ActionController::TestCase
end
class AuditController < ActionController::Base
- before_filter(AuditFilter)
+ before_action(AuditFilter)
def show
render :text => "hello"
@@ -375,14 +375,14 @@ class FilterTest < ActionController::TestCase
end
class AroundFilterController < PrependingController
- around_filter AroundFilter.new
+ around_action AroundFilter.new
end
class BeforeAfterClassFilterController < PrependingController
begin
filter = AroundFilter.new
- before_filter filter
- after_filter filter
+ before_action filter
+ after_action filter
end
end
@@ -394,18 +394,18 @@ class FilterTest < ActionController::TestCase
super()
end
- before_filter { |c| c.class.execution_log << " before procfilter " }
- prepend_around_filter AroundFilter.new
+ before_action { |c| c.class.execution_log << " before procfilter " }
+ prepend_around_action AroundFilter.new
- after_filter { |c| c.class.execution_log << " after procfilter " }
- append_around_filter AppendedAroundFilter.new
+ after_action { |c| c.class.execution_log << " after procfilter " }
+ append_around_action AppendedAroundFilter.new
end
class MixedSpecializationController < ActionController::Base
class OutOfOrder < StandardError; end
- before_filter :first
- before_filter :second, :only => :foo
+ before_action :first
+ before_action :second, :only => :foo
def foo
render :text => 'foo'
@@ -426,7 +426,7 @@ class FilterTest < ActionController::TestCase
end
class DynamicDispatchController < ActionController::Base
- before_filter :choose
+ before_action :choose
%w(foo bar baz).each do |action|
define_method(action) { render :text => action }
@@ -439,9 +439,9 @@ class FilterTest < ActionController::TestCase
end
class PrependingBeforeAndAfterController < ActionController::Base
- prepend_before_filter :before_all
- prepend_after_filter :after_all
- before_filter :between_before_all_and_after_all
+ prepend_before_action :before_all
+ prepend_after_action :after_all
+ before_action :between_before_all_and_after_all
def before_all
@ran_filter ||= []
@@ -473,7 +473,7 @@ class FilterTest < ActionController::TestCase
end
class RescuedController < ActionController::Base
- around_filter RescuingAroundFilterWithBlock.new
+ around_action RescuingAroundFilterWithBlock.new
def show
raise ErrorToRescue.new("Something made the bad noise.")
@@ -482,10 +482,10 @@ class FilterTest < ActionController::TestCase
class NonYieldingAroundFilterController < ActionController::Base
- before_filter :filter_one
- around_filter :non_yielding_filter
- before_filter :filter_two
- after_filter :filter_three
+ before_action :filter_one
+ around_action :non_yielding_action
+ before_action :action_two
+ after_action :action_three
def index
render :inline => "index"
@@ -498,24 +498,24 @@ class FilterTest < ActionController::TestCase
@filters << "filter_one"
end
- def filter_two
- @filters << "filter_two"
+ def action_two
+ @filters << "action_two"
end
- def non_yielding_filter
+ def non_yielding_action
@filters << "it didn't yield"
@filter_return_value
end
- def filter_three
- @filters << "filter_three"
+ def action_three
+ @filters << "action_three"
end
end
class ImplicitActionsController < ActionController::Base
- before_filter :find_only, :only => :edit
- before_filter :find_except, :except => :edit
+ before_action :find_only, :only => :edit
+ before_action :find_except, :except => :edit
private
@@ -528,7 +528,7 @@ class FilterTest < ActionController::TestCase
end
end
- def test_non_yielding_around_filters_not_returning_false_do_not_raise
+ def test_non_yielding_around_actions_not_returning_false_do_not_raise
controller = NonYieldingAroundFilterController.new
controller.instance_variable_set "@filter_return_value", true
assert_nothing_raised do
@@ -536,7 +536,7 @@ class FilterTest < ActionController::TestCase
end
end
- def test_non_yielding_around_filters_returning_false_do_not_raise
+ def test_non_yielding_around_actions_returning_false_do_not_raise
controller = NonYieldingAroundFilterController.new
controller.instance_variable_set "@filter_return_value", false
assert_nothing_raised do
@@ -544,64 +544,64 @@ class FilterTest < ActionController::TestCase
end
end
- def test_after_filters_are_not_run_if_around_filter_returns_false
+ def test_after_actions_are_not_run_if_around_action_returns_false
controller = NonYieldingAroundFilterController.new
controller.instance_variable_set "@filter_return_value", false
test_process(controller, "index")
assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters']
end
- def test_after_filters_are_not_run_if_around_filter_does_not_yield
+ def test_after_actions_are_not_run_if_around_action_does_not_yield
controller = NonYieldingAroundFilterController.new
controller.instance_variable_set "@filter_return_value", true
test_process(controller, "index")
assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters']
end
- def test_added_filter_to_inheritance_graph
- assert_equal [ :ensure_login ], TestController.before_filters
+ def test_added_action_to_inheritance_graph
+ assert_equal [ :ensure_login ], TestController.before_actions
end
def test_base_class_in_isolation
- assert_equal [ ], ActionController::Base.before_filters
+ assert_equal [ ], ActionController::Base.before_actions
end
- def test_prepending_filter
- assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters
+ def test_prepending_action
+ assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_actions
end
- def test_running_filters
+ def test_running_actions
test_process(PrependingController)
assert_equal %w( wonderful_life ensure_login ), assigns["ran_filter"]
end
- def test_running_filters_with_proc
+ def test_running_actions_with_proc
test_process(ProcController)
- assert assigns["ran_proc_filter"]
+ assert assigns["ran_proc_action"]
end
- def test_running_filters_with_implicit_proc
+ def test_running_actions_with_implicit_proc
test_process(ImplicitProcController)
- assert assigns["ran_proc_filter"]
+ assert assigns["ran_proc_action"]
end
- def test_running_filters_with_class
+ def test_running_actions_with_class
test_process(AuditController)
assert assigns["was_audited"]
end
- def test_running_anomolous_yet_valid_condition_filters
+ def test_running_anomolous_yet_valid_condition_actions
test_process(AnomolousYetValidConditionController)
assert_equal %w( ensure_login ), assigns["ran_filter"]
- assert assigns["ran_class_filter"]
- assert assigns["ran_proc_filter1"]
- assert assigns["ran_proc_filter2"]
+ assert assigns["ran_class_action"]
+ assert assigns["ran_proc_action1"]
+ assert assigns["ran_proc_action2"]
- test_process(AnomolousYetValidConditionController, "show_without_filter")
+ test_process(AnomolousYetValidConditionController, "show_without_action")
assert_nil assigns["ran_filter"]
- assert !assigns["ran_class_filter"]
- assert !assigns["ran_proc_filter1"]
- assert !assigns["ran_proc_filter2"]
+ assert !assigns["ran_class_action"]
+ assert !assigns["ran_proc_action1"]
+ assert !assigns["ran_proc_action2"]
end
def test_running_conditional_options
@@ -614,59 +614,59 @@ class FilterTest < ActionController::TestCase
assert_equal %w( ensure_login ), assigns["ran_filter"]
end
- def test_skipping_class_filters
+ def test_skipping_class_actions
test_process(ClassController)
- assert_equal true, assigns["ran_class_filter"]
+ assert_equal true, assigns["ran_class_action"]
skipping_class_controller = Class.new(ClassController) do
- skip_before_filter ConditionalClassFilter
+ skip_before_action ConditionalClassFilter
end
test_process(skipping_class_controller)
- assert_nil assigns['ran_class_filter']
+ assert_nil assigns['ran_class_action']
end
- def test_running_collection_condition_filters
+ def test_running_collection_condition_actions
test_process(ConditionalCollectionFilterController)
assert_equal %w( ensure_login ), assigns["ran_filter"]
- test_process(ConditionalCollectionFilterController, "show_without_filter")
+ test_process(ConditionalCollectionFilterController, "show_without_action")
assert_nil assigns["ran_filter"]
test_process(ConditionalCollectionFilterController, "another_action")
assert_nil assigns["ran_filter"]
end
- def test_running_only_condition_filters
+ def test_running_only_condition_actions
test_process(OnlyConditionSymController)
assert_equal %w( ensure_login ), assigns["ran_filter"]
- test_process(OnlyConditionSymController, "show_without_filter")
+ test_process(OnlyConditionSymController, "show_without_action")
assert_nil assigns["ran_filter"]
test_process(OnlyConditionProcController)
- assert assigns["ran_proc_filter"]
- test_process(OnlyConditionProcController, "show_without_filter")
- assert !assigns["ran_proc_filter"]
+ assert assigns["ran_proc_action"]
+ test_process(OnlyConditionProcController, "show_without_action")
+ assert !assigns["ran_proc_action"]
test_process(OnlyConditionClassController)
- assert assigns["ran_class_filter"]
- test_process(OnlyConditionClassController, "show_without_filter")
- assert !assigns["ran_class_filter"]
+ assert assigns["ran_class_action"]
+ test_process(OnlyConditionClassController, "show_without_action")
+ assert !assigns["ran_class_action"]
end
- def test_running_except_condition_filters
+ def test_running_except_condition_actions
test_process(ExceptConditionSymController)
assert_equal %w( ensure_login ), assigns["ran_filter"]
- test_process(ExceptConditionSymController, "show_without_filter")
+ test_process(ExceptConditionSymController, "show_without_action")
assert_nil assigns["ran_filter"]
test_process(ExceptConditionProcController)
- assert assigns["ran_proc_filter"]
- test_process(ExceptConditionProcController, "show_without_filter")
- assert !assigns["ran_proc_filter"]
+ assert assigns["ran_proc_action"]
+ test_process(ExceptConditionProcController, "show_without_action")
+ assert !assigns["ran_proc_action"]
test_process(ExceptConditionClassController)
- assert assigns["ran_class_filter"]
- test_process(ExceptConditionClassController, "show_without_filter")
- assert !assigns["ran_class_filter"]
+ assert assigns["ran_class_action"]
+ test_process(ExceptConditionClassController, "show_without_action")
+ assert !assigns["ran_class_action"]
end
def test_running_only_condition_and_conditional_options
@@ -674,70 +674,70 @@ class FilterTest < ActionController::TestCase
assert_not assigns["ran_conditional_index_proc"]
end
- def test_running_before_and_after_condition_filters
+ def test_running_before_and_after_condition_actions
test_process(BeforeAndAfterConditionController)
assert_equal %w( ensure_login clean_up_tmp), assigns["ran_filter"]
- test_process(BeforeAndAfterConditionController, "show_without_filter")
+ test_process(BeforeAndAfterConditionController, "show_without_action")
assert_nil assigns["ran_filter"]
end
- def test_around_filter
+ def test_around_action
test_process(AroundFilterController)
assert assigns["before_ran"]
assert assigns["after_ran"]
end
- def test_before_after_class_filter
+ def test_before_after_class_action
test_process(BeforeAfterClassFilterController)
assert assigns["before_ran"]
assert assigns["after_ran"]
end
- def test_having_properties_in_around_filter
+ def test_having_properties_in_around_action
test_process(AroundFilterController)
assert_equal "before and after", assigns["execution_log"]
end
- def test_prepending_and_appending_around_filter
+ def test_prepending_and_appending_around_action
test_process(MixedFilterController)
assert_equal " before aroundfilter before procfilter before appended aroundfilter " +
" after appended aroundfilter after procfilter after aroundfilter ",
MixedFilterController.execution_log
end
- def test_rendering_breaks_filtering_chain
+ def test_rendering_breaks_actioning_chain
response = test_process(RenderingController)
assert_equal "something else", response.body
assert !assigns["ran_action"]
end
- def test_before_filter_rendering_breaks_filtering_chain_for_after_filter
+ def test_before_action_rendering_breaks_actioning_chain_for_after_action
test_process(RenderingController)
- assert_equal %w( before_filter_rendering ), assigns["ran_filter"]
+ assert_equal %w( before_action_rendering ), assigns["ran_filter"]
assert !assigns["ran_action"]
end
- def test_before_filter_redirects_breaks_filtering_chain_for_after_filter
- test_process(BeforeFilterRedirectionController)
+ def test_before_action_redirects_breaks_actioning_chain_for_after_action
+ test_process(BeforeActionRedirectionController)
assert_response :redirect
- assert_equal "http://test.host/filter_test/before_filter_redirection/target_of_redirection", redirect_to_url
- assert_equal %w( before_filter_redirects ), assigns["ran_filter"]
+ assert_equal "http://test.host/filter_test/before_action_redirection/target_of_redirection", redirect_to_url
+ assert_equal %w( before_action_redirects ), assigns["ran_filter"]
end
- def test_before_filter_rendering_breaks_filtering_chain_for_preprend_after_filter
- test_process(RenderingForPrependAfterFilterController)
- assert_equal %w( before_filter_rendering ), assigns["ran_filter"]
+ def test_before_action_rendering_breaks_actioning_chain_for_preprend_after_action
+ test_process(RenderingForPrependAfterActionController)
+ assert_equal %w( before_action_rendering ), assigns["ran_filter"]
assert !assigns["ran_action"]
end
- def test_before_filter_redirects_breaks_filtering_chain_for_preprend_after_filter
- test_process(BeforeFilterRedirectionForPrependAfterFilterController)
+ def test_before_action_redirects_breaks_actioning_chain_for_preprend_after_action
+ test_process(BeforeActionRedirectionForPrependAfterActionController)
assert_response :redirect
- assert_equal "http://test.host/filter_test/before_filter_redirection_for_prepend_after_filter/target_of_redirection", redirect_to_url
- assert_equal %w( before_filter_redirects ), assigns["ran_filter"]
+ assert_equal "http://test.host/filter_test/before_action_redirection_for_prepend_after_action/target_of_redirection", redirect_to_url
+ assert_equal %w( before_action_redirects ), assigns["ran_filter"]
end
- def test_filters_with_mixed_specialization_run_in_order
+ def test_actions_with_mixed_specialization_run_in_order
assert_nothing_raised do
response = test_process(MixedSpecializationController, 'bar')
assert_equal 'bar', response.body
@@ -758,7 +758,7 @@ class FilterTest < ActionController::TestCase
end
end
- def test_running_prepended_before_and_after_filter
+ def test_running_prepended_before_and_after_action
test_process(PrependingBeforeAndAfterController)
assert_equal %w( before_all between_before_all_and_after_all after_all ), assigns["ran_filter"]
end
@@ -775,26 +775,26 @@ class FilterTest < ActionController::TestCase
assert_equal %w( find_record ensure_login ), assigns["ran_filter"]
end
- def test_conditional_skipping_of_filters
+ def test_conditional_skipping_of_actions
test_process(ConditionalSkippingController, "login")
assert_nil assigns["ran_filter"]
test_process(ConditionalSkippingController, "change_password")
assert_equal %w( ensure_login find_user ), assigns["ran_filter"]
test_process(ConditionalSkippingController, "login")
- assert !@controller.instance_variable_defined?("@ran_after_filter")
+ assert !@controller.instance_variable_defined?("@ran_after_action")
test_process(ConditionalSkippingController, "change_password")
- assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_filter")
+ assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_action")
end
- def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional
+ def test_conditional_skipping_of_actions_when_parent_action_is_also_conditional
test_process(ChildOfConditionalParentController)
assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter']
test_process(ChildOfConditionalParentController, 'another_action')
assert_nil assigns['ran_filter']
end
- def test_condition_skipping_of_filters_when_siblings_also_have_conditions
+ def test_condition_skipping_of_actions_when_siblings_also_have_conditions
test_process(ChildOfConditionalParentController)
assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter']
test_process(AnotherChildOfConditionalParentController)
@@ -808,7 +808,7 @@ class FilterTest < ActionController::TestCase
assert_nil assigns['ran_filter']
end
- def test_a_rescuing_around_filter
+ def test_a_rescuing_around_action
response = nil
assert_nothing_raised do
response = test_process(RescuedController)
@@ -818,7 +818,7 @@ class FilterTest < ActionController::TestCase
assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body)
end
- def test_filters_obey_only_and_except_for_implicit_actions
+ def test_actions_obey_only_and_except_for_implicit_actions
test_process(ImplicitActionsController, 'show')
assert_equal 'Except', assigns(:except)
assert_nil assigns(:only)
@@ -852,7 +852,7 @@ class PostsController < ActionController::Base
include AroundExceptions
end
- module_eval %w(raises_before raises_after raises_both no_raise no_filter).map { |action| "def #{action}; default_action end" }.join("\n")
+ module_eval %w(raises_before raises_after raises_both no_raise no_action).map { |action| "def #{action}; default_action end" }.join("\n")
private
def default_action
@@ -861,9 +861,9 @@ class PostsController < ActionController::Base
end
class ControllerWithSymbolAsFilter < PostsController
- around_filter :raise_before, :only => :raises_before
- around_filter :raise_after, :only => :raises_after
- around_filter :without_exception, :only => :no_raise
+ around_action :raise_before, :only => :raises_before
+ around_action :raise_after, :only => :raises_after
+ around_action :without_exception, :only => :no_raise
private
def raise_before
@@ -895,7 +895,7 @@ class ControllerWithFilterClass < PostsController
end
end
- around_filter YieldingFilter, :only => :raises_after
+ around_action YieldingFilter, :only => :raises_after
end
class ControllerWithFilterInstance < PostsController
@@ -906,11 +906,11 @@ class ControllerWithFilterInstance < PostsController
end
end
- around_filter YieldingFilter.new, :only => :raises_after
+ around_action YieldingFilter.new, :only => :raises_after
end
class ControllerWithProcFilter < PostsController
- around_filter(:only => :no_raise) do |c,b|
+ around_action(:only => :no_raise) do |c,b|
c.instance_variable_set(:"@before", true)
b.call
c.instance_variable_set(:"@after", true)
@@ -918,14 +918,14 @@ class ControllerWithProcFilter < PostsController
end
class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
- around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
+ around_action :raise_before, :raise_after, :without_exception, :only => :raises_both
end
class ControllerWithAllTypesOfFilters < PostsController
- before_filter :before
- around_filter :around
- after_filter :after
- around_filter :around_again
+ before_action :before
+ around_action :around
+ after_action :after
+ around_action :around_again
private
def before
@@ -951,8 +951,8 @@ class ControllerWithAllTypesOfFilters < PostsController
end
class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters
- skip_filter :around_again
- skip_filter :after
+ skip_action_callback :around_again
+ skip_action_callback :after
end
class YieldingAroundFiltersTest < ActionController::TestCase
@@ -963,7 +963,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase
assert_nothing_raised { test_process(controller,'no_raise') }
assert_nothing_raised { test_process(controller,'raises_before') }
assert_nothing_raised { test_process(controller,'raises_after') }
- assert_nothing_raised { test_process(controller,'no_filter') }
+ assert_nothing_raised { test_process(controller,'no_action') }
end
def test_with_symbol
@@ -992,7 +992,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase
assert assigns['after']
end
- def test_nested_filters
+ def test_nested_actions
controller = ControllerWithNestedFilters
assert_nothing_raised do
begin
@@ -1008,31 +1008,31 @@ class YieldingAroundFiltersTest < ActionController::TestCase
end
end
- def test_filter_order_with_all_filter_types
+ def test_action_order_with_all_action_types
test_process(ControllerWithAllTypesOfFilters,'no_raise')
assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', assigns['ran_filter'].join(' ')
end
- def test_filter_order_with_skip_filter_method
+ def test_action_order_with_skip_action_method
test_process(ControllerWithTwoLessFilters,'no_raise')
assert_equal 'before around (before yield) around (after yield)', assigns['ran_filter'].join(' ')
end
- def test_first_filter_in_multiple_before_filter_chain_halts
+ def test_first_action_in_multiple_before_action_chain_halts
controller = ::FilterTest::TestMultipleFiltersController.new
response = test_process(controller, 'fail_1')
assert_equal ' ', response.body
assert_equal 1, controller.instance_variable_get(:@try)
end
- def test_second_filter_in_multiple_before_filter_chain_halts
+ def test_second_action_in_multiple_before_action_chain_halts
controller = ::FilterTest::TestMultipleFiltersController.new
response = test_process(controller, 'fail_2')
assert_equal ' ', response.body
assert_equal 2, controller.instance_variable_get(:@try)
end
- def test_last_filter_in_multiple_before_filter_chain_halts
+ def test_last_action_in_multiple_before_action_chain_halts
controller = ::FilterTest::TestMultipleFiltersController.new
response = test_process(controller, 'fail_3')
assert_equal ' ', response.body
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index 7396c850ad..2ddc07ef72 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -81,8 +81,8 @@ module BareMetalTest
assert_nil headers['Content-Length']
end
- test "head :continue (101) does not return a content-type header" do
- headers = HeadController.action(:continue).call(Rack::MockRequest.env_for("/")).second
+ test "head :switching_protocols (101) does not return a content-type header" do
+ headers = HeadController.action(:switching_protocols).call(Rack::MockRequest.env_for("/")).second
assert_nil headers['Content-Type']
assert_nil headers['Content-Length']
end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index b22bc2dc25..9dc6d77012 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -87,7 +87,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_symbols_with_dashes
rs.draw do
get '/:artist/:song-omg', :to => lambda { |env|
- resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
@@ -99,7 +99,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_id_with_dash
rs.draw do
get '/journey/:id', :to => lambda { |env|
- resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
@@ -111,7 +111,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_dash_with_custom_regexp
rs.draw do
get '/:artist/:song-omg', :constraints => { :song => /\d+/ }, :to => lambda { |env|
- resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
@@ -124,7 +124,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_pre_dash
rs.draw do
get '/:artist/omg-:song', :to => lambda { |env|
- resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
@@ -136,7 +136,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_pre_dash_with_custom_regexp
rs.draw do
get '/:artist/omg-:song', :constraints => { :song => /\d+/ }, :to => lambda { |env|
- resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 18a5d8b094..1141feeff7 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -164,7 +164,7 @@ XML
end
class DefaultUrlOptionsCachingController < ActionController::Base
- before_filter { @dynamic_opt = 'opt' }
+ before_action { @dynamic_opt = 'opt' }
def test_url_options_reset
render text: url_for(params)
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 051431ce26..a427113763 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -3368,15 +3368,14 @@ end
class TestAltApp < ActionDispatch::IntegrationTest
class AltRequest
- attr_accessor :path_parameters
+ attr_accessor :path_parameters, :path_info, :script_name
+ attr_reader :env
def initialize(env)
@path_parameters = {}
@env = env
- end
-
- def path_info
- "/"
+ @path_info = "/"
+ @script_name = ""
end
def request_method
diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb
index 92544230b2..f7a06cfed4 100644
--- a/actionpack/test/dispatch/session/mem_cache_store_test.rb
+++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb
@@ -49,6 +49,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal 'foo: "bar"', response.body
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_getting_nil_session_value
@@ -57,6 +59,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal 'foo: nil', response.body
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_getting_session_value_after_session_reset
@@ -76,6 +80,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached"
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_getting_from_nonexistent_session
@@ -85,6 +91,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_equal 'foo: nil', response.body
assert_nil cookies['_session_id'], "should only create session on write, not read"
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_setting_session_value_after_session_reset
@@ -106,6 +114,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
assert_not_equal session_id, response.body
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_getting_session_id
@@ -119,6 +129,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_deserializes_unloaded_class
@@ -133,6 +145,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
end
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_doesnt_write_session_cookie_if_session_id_is_already_exists
@@ -145,6 +159,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
def test_prevents_session_fixation
@@ -160,6 +176,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
assert_response :success
assert_not_equal session_id, cookies['_session_id']
end
+ rescue Dalli::RingError => ex
+ skip ex.message, ex.backtrace
end
rescue LoadError, RuntimeError, Dalli::DalliError
$stderr.puts "Skipping MemCacheStoreTest tests. Start memcached and try again."
diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb
index 910ff8a80f..a4dfd0a63d 100644
--- a/actionpack/test/dispatch/url_generation_test.rb
+++ b/actionpack/test/dispatch/url_generation_test.rb
@@ -15,6 +15,8 @@ module TestUrlGeneration
Routes.draw do
get "/foo", :to => "my_route_generating#index", :as => :foo
+ resources :bars
+
mount MyRouteGeneratingController.action(:index), at: '/bar'
end
@@ -109,6 +111,22 @@ module TestUrlGeneration
test "omit subdomain when key is blank" do
assert_equal "http://example.com/foo", foo_url(subdomain: "")
end
+
+ test "generating URLs with trailing slashes" do
+ assert_equal "/bars.json", bars_path(
+ trailing_slash: true,
+ format: 'json'
+ )
+ end
+
+ test "generating URLS with querystring and trailing slashes" do
+ assert_equal "/bars.json?a=b", bars_path(
+ trailing_slash: true,
+ a: 'b',
+ format: 'json'
+ )
+ end
+
end
end
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index 9a8d644f7b..1a2106a3c5 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -5,23 +5,22 @@ module ActionDispatch
module Journey
class TestRouter < ActiveSupport::TestCase
# TODO : clean up routing tests so we don't need this hack
- class StubDispatcher < Routing::RouteSet::Dispatcher; end
+ class StubDispatcher < Routing::RouteSet::Dispatcher
+ def initialize
+ super({})
+ end
+ def dispatcher?; true; end
+ end
attr_reader :routes
def setup
@app = StubDispatcher.new
@routes = Routes.new
- @router = Router.new(@routes, {})
+ @router = Router.new(@routes)
@formatter = Formatter.new(@routes)
end
- def test_request_class_reader
- klass = Object.new
- router = Router.new(routes, :request_class => klass)
- assert_equal klass, router.request_class
- end
-
class FakeRequestFeeler < Struct.new(:env, :called)
def new env
self.env = env
@@ -39,7 +38,7 @@ module ActionDispatch
end
def test_dashes
- router = Router.new(routes, {})
+ router = Router.new(routes)
exp = Router::Strexp.new '/foo-bar-baz', {}, ['/.?']
path = Path::Pattern.new exp
@@ -55,7 +54,7 @@ module ActionDispatch
end
def test_unicode
- router = Router.new(routes, {})
+ router = Router.new(routes)
#match the escaped version of /ほげ
exp = Router::Strexp.new '/%E3%81%BB%E3%81%92', {}, ['/.?']
@@ -73,7 +72,7 @@ module ActionDispatch
def test_request_class_and_requirements_success
klass = FakeRequestFeeler.new nil
- router = Router.new(routes, {:request_class => klass })
+ router = Router.new(routes)
requirements = { :hello => /world/ }
@@ -82,7 +81,7 @@ module ActionDispatch
routes.add_route nil, path, requirements, {:id => nil}, {}
- env = rails_env 'PATH_INFO' => '/foo/10'
+ env = rails_env({'PATH_INFO' => '/foo/10'}, klass)
router.recognize(env) do |r, params|
assert_equal({:id => '10'}, params)
end
@@ -93,7 +92,7 @@ module ActionDispatch
def test_request_class_and_requirements_fail
klass = FakeRequestFeeler.new nil
- router = Router.new(routes, {:request_class => klass })
+ router = Router.new(routes)
requirements = { :hello => /mom/ }
@@ -102,7 +101,7 @@ module ActionDispatch
router.routes.add_route nil, path, requirements, {:id => nil}, {}
- env = rails_env 'PATH_INFO' => '/foo/10'
+ env = rails_env({'PATH_INFO' => '/foo/10'}, klass)
router.recognize(env) do |r, params|
flunk 'route should not be found'
end
@@ -111,21 +110,26 @@ module ActionDispatch
assert_equal env.env, klass.env
end
- class CustomPathRequest < Router::NullReq
+ class CustomPathRequest < ActionDispatch::Request
def path_info
env['custom.path_info']
end
+
+ def path_info=(x)
+ env['custom.path_info'] = x
+ end
end
def test_request_class_overrides_path_info
- router = Router.new(routes, {:request_class => CustomPathRequest })
+ router = Router.new(routes)
exp = Router::Strexp.new '/bar', {}, ['/.?']
path = Path::Pattern.new exp
routes.add_route nil, path, {}, {}, {}
- env = rails_env 'PATH_INFO' => '/foo', 'custom.path_info' => '/bar'
+ env = rails_env({'PATH_INFO' => '/foo',
+ 'custom.path_info' => '/bar'}, CustomPathRequest)
recognized = false
router.recognize(env) do |r, params|
@@ -207,20 +211,23 @@ module ActionDispatch
def test_X_Cascade
add_routes @router, [ "/messages(.:format)" ]
- resp = @router.call({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' })
+ resp = @router.serve(rails_env({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' }))
assert_equal ['Not Found'], resp.last
assert_equal 'pass', resp[1]['X-Cascade']
assert_equal 404, resp.first
end
def test_clear_trailing_slash_from_script_name_on_root_unanchored_routes
+ route_set = Routing::RouteSet.new
+ mapper = Routing::Mapper.new route_set
+
strexp = Router::Strexp.new("/", {}, ['/', '.', '?'], false)
path = Path::Pattern.new strexp
app = lambda { |env| [200, {}, ['success!']] }
- @router.routes.add_route(app, path, {}, {}, {})
+ mapper.get '/weblog', :to => app
env = rack_env('SCRIPT_NAME' => '', 'PATH_INFO' => '/weblog')
- resp = @router.call(env)
+ resp = route_set.call env
assert_equal ['success!'], resp.last
assert_equal '', env['SCRIPT_NAME']
end
@@ -561,8 +568,8 @@ module ActionDispatch
RailsEnv = Struct.new(:env)
- def rails_env env
- RailsEnv.new rack_env env
+ def rails_env env, klass = ActionDispatch::Request
+ klass.new env
end
def rack_env env
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index 824cdaa45e..f53cce32b0 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -11,7 +11,7 @@ module ActionView
# the assets exist before linking to them:
#
# image_tag("rails.png")
- # # => <img alt="Rails" src="/assets/rails.png" />
+ # # => <img alt="Rails" src="/images/rails.png" />
# stylesheet_link_tag("application")
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
module AssetTagHelper
@@ -20,7 +20,8 @@ module ActionView
include AssetUrlHelper
include TagHelper
- # Returns an HTML script tag for each of the +sources+ provided.
+ # Returns an HTML script tag for each of the +sources+ provided. If
+ # you don't specify an extension, <tt>.js</tt> will be appended automatically.
#
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
# to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
@@ -33,19 +34,19 @@ module ActionView
# last argument.
#
# When the Asset Pipeline is enabled, you can pass the name of your manifest as
- # source, and include other JavaScript or CoffeeScript files inside the manifest.
+ # source and include other JavaScript or CoffeeScript files inside the manifest.
#
# javascript_include_tag "xmlhr"
- # # => <script src="/assets/xmlhr.js?1284139606"></script>
+ # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
#
# javascript_include_tag "template.jst", extname: false
- # # => <script src="/assets/template.jst?1284139606"></script>
+ # # => <script src="/javascripts/template.jst?1284139606"></script>
#
# javascript_include_tag "xmlhr.js"
- # # => <script src="/assets/xmlhr.js?1284139606"></script>
+ # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
#
# javascript_include_tag "common.javascript", "/elsewhere/cools"
- # # => <script src="/assets/common.javascript?1284139606"></script>
+ # # => <script src="/javascripts/common.javascript?1284139606"></script>
# # <script src="/elsewhere/cools.js?1423139606"></script>
#
# javascript_include_tag "http://www.example.com/xmlhr"
@@ -72,22 +73,22 @@ module ActionView
# apply to all media types.
#
# stylesheet_link_tag "style"
- # # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
+ # # => <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
#
# stylesheet_link_tag "style.css"
- # # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
+ # # => <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
#
# stylesheet_link_tag "http://www.example.com/style.css"
# # => <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
#
# stylesheet_link_tag "style", media: "all"
- # # => <link href="/assets/style.css" media="all" rel="stylesheet" />
+ # # => <link href="/stylesheets/style.css" media="all" rel="stylesheet" />
#
# stylesheet_link_tag "style", media: "print"
- # # => <link href="/assets/style.css" media="print" rel="stylesheet" />
+ # # => <link href="/stylesheets/style.css" media="print" rel="stylesheet" />
#
# stylesheet_link_tag "random.styles", "/css/stylish"
- # # => <link href="/assets/random.styles" media="screen" rel="stylesheet" />
+ # # => <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />
# # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
def stylesheet_link_tag(*sources)
options = sources.extract_options!.stringify_keys
@@ -158,17 +159,17 @@ module ActionView
# respectively:
#
# favicon_link_tag
- # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon" />
+ # # => <link href="/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
#
# favicon_link_tag 'myicon.ico'
- # # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/x-icon" />
+ # # => <link href="/images/myicon.ico" rel="shortcut icon" type="image/x-icon" />
#
# Mobile Safari looks for a different link tag, pointing to an image that
# will be used if you add the page to the home screen of an iOS device.
# The following call would generate such a tag:
#
# favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
- # # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
+ # # => <link href="/images/mb-icon.png" rel="apple-touch-icon" type="image/png" />
def favicon_link_tag(source='favicon.ico', options={})
tag('link', {
:rel => 'shortcut icon',
@@ -258,19 +259,19 @@ module ActionView
# ==== Examples
#
# video_tag("trailer")
- # # => <video src="/videos/trailer" />
+ # # => <video src="/videos/trailer"></video>
# video_tag("trailer.ogg")
- # # => <video src="/videos/trailer.ogg" />
+ # # => <video src="/videos/trailer.ogg"></video>
# video_tag("trailer.ogg", controls: true, autobuffer: true)
- # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
+ # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" ></video>
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
- # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
+ # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png"></video>
# video_tag("/trailers/hd.avi", size: "16x16")
- # # => <video src="/trailers/hd.avi" width="16" height="16" />
+ # # => <video src="/trailers/hd.avi" width="16" height="16"></video>
# video_tag("/trailers/hd.avi", size: "16")
- # # => <video height="16" src="/trailers/hd.avi" width="16" />
+ # # => <video height="16" src="/trailers/hd.avi" width="16"></video>
# video_tag("/trailers/hd.avi", height: '32', width: '32')
- # # => <video height="32" src="/trailers/hd.avi" width="32" />
+ # # => <video height="32" src="/trailers/hd.avi" width="32"></video>
# video_tag("trailer.ogg", "trailer.flv")
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
# video_tag(["trailer.ogg", "trailer.flv"])
@@ -289,11 +290,11 @@ module ActionView
# your public audios directory.
#
# audio_tag("sound")
- # # => <audio src="/audios/sound" />
+ # # => <audio src="/audios/sound"></audio>
# audio_tag("sound.wav")
- # # => <audio src="/audios/sound.wav" />
+ # # => <audio src="/audios/sound.wav"></audio>
# audio_tag("sound.wav", autoplay: true, controls: true)
- # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
+ # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav"></audio>
# audio_tag("sound.wav", "sound.mid")
# # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
def audio_tag(*sources)
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index ae684af87b..d86e7e490c 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -7,10 +7,10 @@ module ActionView
# urls.
#
# image_path("rails.png")
- # # => "/assets/rails.png"
+ # # => "/images/rails.png"
#
# image_url("rails.png")
- # # => "http://www.example.com/assets/rails.png"
+ # # => "http://www.example.com/images/rails.png"
#
# === Using asset hosts
#
@@ -113,13 +113,13 @@ module ActionView
#
# All other asset *_path helpers delegate through this method.
#
- # asset_path "application.js" # => /application.js
- # asset_path "application", type: :javascript # => /javascripts/application.js
- # asset_path "application", type: :stylesheet # => /stylesheets/application.css
+ # asset_path "application.js" # => /assets/application.js
+ # asset_path "application", type: :javascript # => /assets/application.js
+ # asset_path "application", type: :stylesheet # => /assets/application.css
# asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
def asset_path(source, options = {})
- source = source.to_s
return "" unless source.present?
+ source = source.to_s
return source if source =~ URI_REGEXP
tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
@@ -153,7 +153,7 @@ module ActionView
# All other options provided are forwarded to +asset_path+ call.
#
# asset_url "application.js" # => http://example.com/application.js
- # asset_url "application.js", host: "http://cdn.example.com" # => http://cdn.example.com/javascripts/application.js
+ # asset_url "application.js", host: "http://cdn.example.com" # => http://cdn.example.com/assets/application.js
#
def asset_url(source, options = {})
path_to_asset(source, options.merge(:protocol => :request))
@@ -231,7 +231,7 @@ module ActionView
# Computes the path to a javascript asset in the public javascripts directory.
# If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
# Full paths from the document root will be passed through.
- # Used internally by javascript_include_tag to build the script path.
+ # Used internally by +javascript_include_tag+ to build the script path.
#
# javascript_path "xmlhr" # => /javascripts/xmlhr.js
# javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
@@ -251,7 +251,7 @@ module ActionView
alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
# Computes the path to a stylesheet asset in the public stylesheets directory.
- # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
+ # If the +source+ filename has no extension, .css will be appended (except for explicit URIs).
# Full paths from the document root will be passed through.
# Used internally by +stylesheet_link_tag+ to build the stylesheet path.
#
@@ -276,9 +276,9 @@ module ActionView
# Full paths from the document root will be passed through.
# Used internally by +image_tag+ to build the image path:
#
- # image_path("edit") # => "/assets/edit"
- # image_path("edit.png") # => "/assets/edit.png"
- # image_path("icons/edit.png") # => "/assets/icons/edit.png"
+ # image_path("edit") # => "/images/edit"
+ # image_path("edit.png") # => "/images/edit.png"
+ # image_path("icons/edit.png") # => "/images/icons/edit.png"
# image_path("/icons/edit.png") # => "/icons/edit.png"
# image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
#
@@ -342,9 +342,9 @@ module ActionView
# Computes the path to a font asset.
# Full paths from the document root will be passed through.
#
- # font_path("font") # => /assets/font
- # font_path("font.ttf") # => /assets/font.ttf
- # font_path("dir/font.ttf") # => /assets/dir/font.ttf
+ # font_path("font") # => /fonts/font
+ # font_path("font.ttf") # => /fonts/font.ttf
+ # font_path("dir/font.ttf") # => /fonts/dir/font.ttf
# font_path("/dir/font.ttf") # => /dir/font.ttf
# font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
def font_path(source, options = {})
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 10dcb5c28c..1e818083cc 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -109,8 +109,8 @@ module ActionView
# # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
# # <option>Out</option></select>
#
- # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input'
- # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
+ # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input', id: 'unique_id'
+ # # => <select class="form_input" id="unique_id" multiple="multiple" name="access[]"><option>Read</option>
# # <option>Write</option></select>
#
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb
index 017302d40f..c92d090cce 100644
--- a/actionview/lib/action_view/rendering.rb
+++ b/actionview/lib/action_view/rendering.rb
@@ -62,8 +62,8 @@ module ActionView
#
# The view class must have the following methods:
# View.new[lookup_context, assigns, controller]
- # Create a new ActionView instance for a controller
- # View#render[options]
+ # Create a new ActionView instance for a controller and we can also pass the arguments.
+ # View#render(option)
# Returns String with the rendered template
#
# Override this method in a module to change the default behavior.
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 3dd973d64b..736745c3cd 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,31 @@
+* Fix redefine a has_and_belongs_to_many inside inherited class
+ Fixing regression case, where redefining the same has_an_belongs_to_many
+ definition into a subclass would raise.
+
+ Fixes #14983.
+
+ *arthurnn*
+
+* Add a properties API to allow custom types and type casting behavior
+ to be specified. Will enable many edge cases to be deprecated, and
+ allow for additional interesting features in the future.
+
+ *Sean Griffin*
+
+* Fix has_and_belongs_to_many public reflection.
+ When defining a has_and_belongs_to_many, internally we convert that to two has_many.
+ But as `reflections` is a public API, people expect to see the right macro.
+
+ Fixes #14682.
+
+ *arthurnn*
+
+* Fixed serialization for records with an attribute named `format`.
+
+ Fixes #15188.
+
+ *Godfrey Chan*
+
* When a `group` is set, `sum`, `size`, `average`, `minimum` and `maximum`
on a NullRelation should return a Hash.
@@ -16,13 +44,14 @@
* Change belongs_to touch to be consistent with timestamp updates
- If a model is set up with a belongs_to: touch relatinoship the parent
+ If a model is set up with a belongs_to: touch relationship the parent
record will only be touched if the record was modified. This makes it
consistent with timestamp updating on the record itself.
*Brock Trappitt*
-* Fixed the inferred table name of a HABTM auxiliar table inside a schema.
+* Fixed the inferred table name of a has_and_belongs_to_many auxiliar
+ table inside a schema.
Fixes #14824
@@ -65,7 +94,7 @@
*Aaron Nelson*
-* Fix how to calculate associated class name when using namespaced `has_and_belongs_to_many`
+* Fix how to calculate associated class name when using namespaced has_and_belongs_to_many
association.
Fixes #14709.
@@ -118,7 +147,7 @@
*Innokenty Mikhailov*
-* Allow the PostgreSQL adapter to handle bigserial pk types again.
+* Allow the PostgreSQL adapter to handle bigserial primary key types again.
Fixes #10410.
@@ -133,10 +162,10 @@
*Yves Senn*
-* Fixed HABTM's CollectionAssociation size calculation.
+* Fixed has_and_belongs_to_many's CollectionAssociation size calculation.
- HABTM should fall back to using the normal CollectionAssociation's size
- calculation if the collection is not cached or loaded.
+ has_and_belongs_to_many should fall back to using the normal CollectionAssociation's
+ size calculation if the collection is not cached or loaded.
Fixes #14913, #14914.
@@ -180,10 +209,10 @@
*Eric Chahin*, *Aaron Nelson*, *Kevin Casey*
-* Stringify all variable keys of mysql connection configuration.
+* Stringify all variables keys of MySQL connection configuration.
- When the `sql_mode` variable for mysql adapters is set in the configuration
- as a `String`, it was ignored and overwritten by the strict mode option.
+ When `sql_mode` variable for MySQL adapters set in configuration as `String`
+ was ignored and overwritten by strict mode option.
Fixes #14895.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 727ee5f65f..07dfc448e7 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -50,7 +50,7 @@ module ActiveRecord
def initialize(reflection)
through_reflection = reflection.through_reflection
source_reflection_names = reflection.source_reflection_names
- source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
+ source_associations = reflection.through_reflection.klass._reflections.keys
super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
end
end
@@ -151,7 +151,7 @@ module ActiveRecord
association = association_instance_get(name)
if association.nil?
- raise AssociationNotFoundError.new(self, name) unless reflection = self.class.reflect_on_association(name)
+ raise AssociationNotFoundError.new(self, name) unless reflection = self.class._reflect_on_association(name)
association = reflection.association_class.new(self, reflection)
association_instance_set(name, association)
end
@@ -202,12 +202,13 @@ module ActiveRecord
# For instance, +attributes+ and +connection+ would be bad choices for association names.
#
# == Auto-generated methods
+ # See also Instance Public methods below for more details.
#
# === Singular associations (one-to-one)
# | | belongs_to |
# generated methods | belongs_to | :polymorphic | has_one
# ----------------------------------+------------+--------------+---------
- # other | X | X | X
+ # other(force_reload=false) | X | X | X
# other=(other) | X | X | X
# build_other(attributes={}) | X | | X
# create_other(attributes={}) | X | | X
@@ -217,7 +218,7 @@ module ActiveRecord
# | | | has_many
# generated methods | habtm | has_many | :through
# ----------------------------------+-------+----------+----------
- # others | X | X | X
+ # others(force_reload=false) | X | X | X
# others=(other,other,...) | X | X | X
# other_ids | X | X | X
# other_ids=(id,id,...) | X | X | X
@@ -1576,6 +1577,8 @@ module ActiveRecord
scope = nil
end
+ habtm_reflection = ActiveRecord::Reflection::AssociationReflection.new(:has_and_belongs_to_many, name, scope, options, self)
+
builder = Builder::HasAndBelongsToMany.new name, self, options
join_model = builder.through_model
@@ -1589,6 +1592,7 @@ module ActiveRecord
Builder::HasMany.define_callbacks self, middle_reflection
Reflection.add_reflection self, middle_reflection.name, middle_reflection
+ middle_reflection.parent_reflection = [name.to_s, habtm_reflection]
include Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -1609,6 +1613,7 @@ module ActiveRecord
end
has_many name, scope, hm_options, &extension
+ self._reflections[name.to_s].parent_reflection = [name.to_s, habtm_reflection]
end
end
end
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 85109aee6c..a6a1947148 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -32,8 +32,18 @@ module ActiveRecord
join.left.downcase.scan(
/join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
).size
- else
+ elsif join.respond_to? :left
join.left.table_name == name ? 1 : 0
+ else
+ # this branch is reached by two tests:
+ #
+ # activerecord/test/cases/associations/cascaded_eager_loading_test.rb:37
+ # with :posts
+ #
+ # activerecord/test/cases/associations/eager_test.rb:1133
+ # with :comments
+ #
+ 0
end
end
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 9ad2d2fb12..4a04303fb8 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -160,7 +160,7 @@ module ActiveRecord
def marshal_load(data)
reflection_name, ivars = data
ivars.each { |name, val| instance_variable_set(name, val) }
- @reflection = @owner.class.reflect_on_association(reflection_name)
+ @reflection = @owner.class._reflect_on_association(reflection_name)
end
def initialize_attributes(record) #:nodoc:
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 30b11c01eb..0ad5206980 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -66,13 +66,13 @@ module ActiveRecord::Associations::Builder
def self.add_left_association(name, options)
belongs_to name, options
- self.left_reflection = reflect_on_association(name)
+ self.left_reflection = _reflect_on_association(name)
end
def self.add_right_association(name, options)
rhs_name = name.to_s.singularize.to_sym
belongs_to rhs_name, options
- self.right_reflection = reflect_on_association(rhs_name)
+ self.right_reflection = _reflect_on_association(rhs_name)
end
}
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index f5e911c739..2727e23870 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -100,7 +100,8 @@ module ActiveRecord
# Hence this method.
def inverse_updates_counter_cache?(reflection = reflection())
counter_name = cached_counter_attribute_name(reflection)
- reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
+ reflection.klass._reflections.values.any? { |inverse_reflection|
+ :belongs_to == inverse_reflection.macro &&
inverse_reflection.counter_cache_column == counter_name
}
end
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 5842be3a7b..01173b68f3 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -207,7 +207,7 @@ module ActiveRecord
end
def find_reflection(klass, name)
- klass.reflect_on_association(name) or
+ klass._reflect_on_association(name) or
raise ConfigurationError, "Association named '#{ name }' was not found on #{ klass.name }; perhaps you misspelled it?"
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 42571d6af0..7519fec10a 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -143,6 +143,7 @@ module ActiveRecord
def grouped_records(association, records)
h = {}
records.each do |record|
+ next unless record
assoc = record.association(association)
klasses = h[assoc.reflection] ||= {}
(klasses[assoc.klass] ||= []) << record
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 6c2403d87e..a0a0214eae 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -48,7 +48,11 @@ module ActiveRecord
end
private
- def method_body; raise NotImplementedError; end
+
+ # Override this method in the subclasses for method body.
+ def method_body(method_name, const_name)
+ raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
+ end
end
module ClassMethods
@@ -66,6 +70,7 @@ module ActiveRecord
# Generates all the attribute related methods for columns in the database
# accessors, mutators and query methods.
def define_attribute_methods # :nodoc:
+ return false if @attribute_methods_generated
# Use a mutex; we don't want two thread simultaneously trying to define
# attribute methods.
generated_attribute_methods.synchronize do
@@ -456,7 +461,7 @@ module ActiveRecord
end
def pk_attribute?(name)
- column_for_attribute(name).primary
+ name == self.class.primary_key
end
def typecasted_attribute_value(name)
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 53a9c874bf..47c6f94ba7 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -65,6 +65,8 @@ module ActiveRecord
end
class Type # :nodoc:
+ delegate :type, :type_cast_for_database, to: :@column
+
def initialize(column)
@column = column
end
@@ -77,10 +79,6 @@ module ActiveRecord
end
end
- def type
- @column.type
- end
-
def accessor
ActiveRecord::Store::IndifferentHashAccessor
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 dfebb2cf56..6149ac4906 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -2,6 +2,8 @@ module ActiveRecord
module AttributeMethods
module TimeZoneConversion
class Type # :nodoc:
+ delegate :type, :type_cast_for_database, to: :@column
+
def initialize(column)
@column = column
end
@@ -10,10 +12,6 @@ module ActiveRecord
value = @column.type_cast(value)
value.acts_like?(:time) ? value.in_time_zone : value
end
-
- def type
- @column.type
- end
end
extend ActiveSupport::Concern
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 1a4d2957ec..74e2a8e6b9 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -147,6 +147,7 @@ module ActiveRecord
private
def define_non_cyclic_method(name, &block)
+ return if method_defined?(name)
define_method(name) do |*args|
result = true; @_already_called ||= {}
# Loop prevention for validation of associations
@@ -179,30 +180,28 @@ module ActiveRecord
validation_method = :"validate_associated_records_for_#{reflection.name}"
collection = reflection.collection?
- unless method_defined?(save_method)
- if collection
- before_save :before_save_collection_association
-
- define_non_cyclic_method(save_method) { save_collection_association(reflection) }
- # Doesn't use after_save as that would save associations added in after_create/after_update twice
- after_create save_method
- after_update save_method
- elsif reflection.macro == :has_one
- define_method(save_method) { save_has_one_association(reflection) }
- # Configures two callbacks instead of a single after_save so that
- # the model may rely on their execution order relative to its
- # own callbacks.
- #
- # For example, given that after_creates run before after_saves, if
- # we configured instead an after_save there would be no way to fire
- # a custom after_create callback after the child association gets
- # created.
- after_create save_method
- after_update save_method
- else
- define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
- before_save save_method
- end
+ if collection
+ before_save :before_save_collection_association
+
+ define_non_cyclic_method(save_method) { save_collection_association(reflection) }
+ # Doesn't use after_save as that would save associations added in after_create/after_update twice
+ after_create save_method
+ after_update save_method
+ elsif reflection.macro == :has_one
+ define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
+ # Configures two callbacks instead of a single after_save so that
+ # the model may rely on their execution order relative to its
+ # own callbacks.
+ #
+ # For example, given that after_creates run before after_saves, if
+ # we configured instead an after_save there would be no way to fire
+ # a custom after_create callback after the child association gets
+ # created.
+ after_create save_method
+ after_update save_method
+ else
+ define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
+ before_save save_method
end
if reflection.validate? && !method_defined?(validation_method)
@@ -273,9 +272,11 @@ module ActiveRecord
# go through nested autosave associations that are loaded in memory (without loading
# any new ones), and return true if is changed for autosave
def nested_records_changed_for_autosave?
- self.class.reflect_on_all_autosave_associations.any? do |reflection|
- association = association_instance_get(reflection.name)
- association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
+ self.class._reflections.values.any? do |reflection|
+ if reflection.options[:autosave]
+ association = association_instance_get(reflection.name)
+ association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
+ end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index db4d5f0129..8b0fffcf06 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -19,6 +19,7 @@ require 'active_record/errors'
require 'active_record/log_subscriber'
require 'active_record/explain_subscriber'
require 'active_record/relation/delegation'
+require 'active_record/properties'
module ActiveRecord #:nodoc:
# = Active Record
@@ -321,6 +322,7 @@ module ActiveRecord #:nodoc:
include Reflection
include Serialization
include Store
+ include Properties
end
ActiveSupport.run_load_hooks(:active_record, Base)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 75501852ed..f836e60988 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -9,26 +9,22 @@ module ActiveRecord
# records are quoted as their primary key
return value.quoted_id if value.respond_to?(:quoted_id)
+ # FIXME: The only case we get an object other than nil or a real column
+ # is `SchemaStatements#add_column` with a PG array that has a non-empty default
+ # value. Is this really the only case? Are we missing tests for other types?
+ # We should have a real column object passed (or nil) here, and check for that
+ # instead
+ if column.respond_to?(:type_cast_for_database)
+ value = column.type_cast_for_database(value)
+ end
+
case value
when String, ActiveSupport::Multibyte::Chars
- value = value.to_s
- return "'#{quote_string(value)}'" unless column
-
- case column.type
- when :integer then value.to_i.to_s
- when :float then value.to_f.to_s
- else
- "'#{quote_string(value)}'"
- end
-
- when true, false
- if column && column.type == :integer
- value ? '1' : '0'
- else
- value ? quoted_true : quoted_false
- end
- # BigDecimals need to be put in a non-normalized form and quoted.
+ "'#{quote_string(value.to_s)}'"
+ when true then quoted_true
+ when false then quoted_false
when nil then "NULL"
+ # BigDecimals need to be put in a non-normalized form and quoted.
when BigDecimal then value.to_s('F')
when Numeric, ActiveSupport::Duration then value.to_s
when Date, Time then "'#{quoted_date(value)}'"
@@ -47,30 +43,25 @@ module ActiveRecord
return value.id
end
- case value
- when String, ActiveSupport::Multibyte::Chars
- value = value.to_s
- return value unless column
-
- case column.type
- when :integer then value.to_i
- when :float then value.to_f
- else
- value
- end
+ # FIXME: The only case we get an object other than nil or a real column
+ # is `SchemaStatements#add_column` with a PG array that has a non-empty default
+ # value. Is this really the only case? Are we missing tests for other types?
+ # We should have a real column object passed (or nil) here, and check for that
+ # instead
+ if column.respond_to?(:type_cast_for_database)
+ value = column.type_cast_for_database(value)
+ end
- when true, false
- if column && column.type == :integer
- value ? 1 : 0
- else
- value ? 't' : 'f'
- end
- # BigDecimals need to be put in a non-normalized form and quoted.
- when nil then nil
+ case value
+ when Symbol, ActiveSupport::Multibyte::Chars
+ value.to_s
+ when true then unquoted_true
+ when false then unquoted_false
+ # BigDecimals need to be put in a non-normalized form and quoted.
when BigDecimal then value.to_s('F')
- when Numeric then value
when Date, Time then quoted_date(value)
- when Symbol then value.to_s
+ when *types_which_need_no_typecasting
+ value
else
to_type = column ? " to #{column.type}" : ""
raise TypeError, "can't cast #{value.class}#{to_type}"
@@ -109,10 +100,18 @@ module ActiveRecord
"'t'"
end
+ def unquoted_true
+ 't'
+ end
+
def quoted_false
"'f'"
end
+ def unquoted_false
+ 'f'
+ end
+
def quoted_date(value)
if value.acts_like?(:time)
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -124,6 +123,12 @@ module ActiveRecord
value.to_s(:db)
end
+
+ private
+
+ def types_which_need_no_typecasting
+ [nil, Numeric, String]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index cdf0cbe218..ac14740cfe 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -20,15 +20,8 @@ module ActiveRecord
def prepare_column_options(column, types)
spec = {}
spec[:name] = column.name.inspect
-
- # AR has an optimization which handles zero-scale decimals as integers. This
- # code ensures that the dumper still dumps the column as a decimal.
- spec[:type] = if column.type == :integer && /^(numeric|decimal)/ =~ column.sql_type
- 'decimal'
- else
- column.type.to_s
- end
- spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit] && spec[:type] != 'decimal'
+ spec[:type] = column.type.to_s
+ spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit]
spec[:precision] = column.precision.inspect if column.precision
spec[:scale] = column.scale.inspect if column.scale
spec[:null] = 'false' unless column.null
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index ea7703c82c..6ecd4efdc8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -396,7 +396,8 @@ module ActiveRecord
precision = extract_precision(sql_type)
if scale == 0
- Type::Integer.new(precision: precision)
+ # FIXME: Remove this class as well
+ Type::DecimalWithoutScale.new(precision: precision)
else
Type::Decimal.new(precision: precision, scale: scale)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index d3f8470c30..82e62786ca 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -178,17 +178,6 @@ module ActiveRecord
true
end
- def type_cast(value, column)
- case value
- when TrueClass
- 1
- when FalseClass
- 0
- else
- super
- end
- end
-
# MySQL 4 technically support transaction isolation, but it is affected by a bug
# where the transaction level gets persisted for the whole session:
#
@@ -234,8 +223,6 @@ module ActiveRecord
if value.kind_of?(String) && column && column.type == :binary
s = value.unpack("H*")[0]
"x'#{s}'"
- elsif value.kind_of?(BigDecimal)
- value.to_s("F")
else
super
end
@@ -253,10 +240,18 @@ module ActiveRecord
QUOTED_TRUE
end
+ def unquoted_true
+ 1
+ end
+
def quoted_false
QUOTED_FALSE
end
+ def unquoted_false
+ 0
+ end
+
# REFERENTIAL INTEGRITY ====================================
def disable_referential_integrity #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 704868c058..86232f9d3f 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -14,11 +14,12 @@ module ActiveRecord
end
attr_reader :name, :default, :cast_type, :null, :sql_type, :default_function
- attr_accessor :primary, :coder
+ attr_accessor :coder
alias :encoded? :coder
- delegate :type, :precision, :scale, :limit, :klass, :text?, :number?, :binary?, :type_cast_for_write, to: :cast_type
+ delegate :type, :precision, :scale, :limit, :klass, :text?, :number?, :binary?,
+ :type_cast_for_write, :type_cast_for_database, to: :cast_type
# Instantiates a new column in the table.
#
@@ -36,7 +37,6 @@ module ActiveRecord
@null = null
@default = extract_default(default)
@default_function = nil
- @primary = nil
@coder = nil
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index 0cbedb0987..f7bad20f00 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -6,13 +6,6 @@ module ActiveRecord
"(#{point[0]},#{point[1]})"
end
- def string_to_point(string) # :nodoc:
- if string[0] == '(' && string[-1] == ')'
- string = string[1...-1]
- end
- string.split(',').map{ |v| Float(v) }
- end
-
def string_to_bit(value) # :nodoc:
case value
when /^0x/i
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index b55766bde0..9a5e2d05ef 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -6,18 +6,16 @@ module ActiveRecord
class PostgreSQLColumn < Column #:nodoc:
attr_accessor :array
- def initialize(name, default, cast_type, sql_type = nil, null = true)
- default_value = self.class.extract_value_from_default(default)
-
+ def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
if sql_type =~ /\[\]$/
@array = true
- super(name, default_value, cast_type, sql_type[0..sql_type.length - 3], null)
+ super(name, default, cast_type, sql_type[0..sql_type.length - 3], null)
else
@array = false
- super(name, default_value, cast_type, sql_type, null)
+ super(name, default, cast_type, sql_type, null)
end
- @default_function = default if has_default_function?(default_value, default)
+ @default_function = default_function
end
# :stopdoc:
@@ -38,78 +36,9 @@ module ActiveRecord
end
# :startdoc:
- # Extracts the value from a PostgreSQL column default definition.
- def self.extract_value_from_default(default)
- # This is a performance optimization for Ruby 1.9.2 in development.
- # If the value is nil, we return nil straight away without checking
- # the regular expressions. If we check each regular expression,
- # Regexp#=== will call NilClass#to_str, which will trigger
- # method_missing (defined by whiny nil in ActiveSupport) which
- # makes this method very very slow.
- return default unless default
-
- case default
- when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
- $1
- # Numeric types
- when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
- $1
- # Character types
- when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
- $1.gsub(/''/, "'")
- # Binary data types
- when /\A'(.*)'::bytea\z/m
- $1
- # Date/time types
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
- $1
- when /\A'(.*)'::interval\z/
- $1
- # Boolean type
- when 'true'
- true
- when 'false'
- false
- # Geometric types
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
- $1
- # Network address types
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
- $1
- # Bit string types
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
- $1
- # XML type
- when /\A'(.*)'::xml\z/m
- $1
- # Arrays
- when /\A'(.*)'::"?\D+"?\[\]\z/
- $1
- # Hstore
- when /\A'(.*)'::hstore\z/
- $1
- # JSON
- when /\A'(.*)'::json\z/
- $1
- # Object identifier types
- when /\A-?\d+\z/
- $1
- else
- # Anything else is blank, some user type, or some function
- # and we can't know the value of that, so return nil.
- nil
- end
- end
-
def accessor
cast_type.accessor
end
-
- private
-
- def has_default_function?(default_value, default)
- !default_value && (%r{\w+\(.*\)} === default)
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
index 2769a8d3b4..f9531ddee3 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
@@ -5,7 +5,10 @@ module ActiveRecord
class Point < Type::String
def type_cast(value)
if ::String === value
- ConnectionAdapters::PostgreSQLColumn.string_to_point value
+ if value[0] == '(' && value[-1] == ')'
+ value = value[1...-1]
+ end
+ value.split(',').map{ |v| Float(v) }
else
value
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
new file mode 100644
index 0000000000..bcfd605165
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -0,0 +1,134 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module PostgreSQL
+ module ColumnMethods
+ def xml(*args)
+ options = args.extract_options!
+ column(args[0], 'xml', options)
+ end
+
+ def tsvector(*args)
+ options = args.extract_options!
+ column(args[0], 'tsvector', options)
+ end
+
+ def int4range(name, options = {})
+ column(name, 'int4range', options)
+ end
+
+ def int8range(name, options = {})
+ column(name, 'int8range', options)
+ end
+
+ def tsrange(name, options = {})
+ column(name, 'tsrange', options)
+ end
+
+ def tstzrange(name, options = {})
+ column(name, 'tstzrange', options)
+ end
+
+ def numrange(name, options = {})
+ column(name, 'numrange', options)
+ end
+
+ def daterange(name, options = {})
+ column(name, 'daterange', options)
+ end
+
+ def hstore(name, options = {})
+ column(name, 'hstore', options)
+ end
+
+ def ltree(name, options = {})
+ column(name, 'ltree', options)
+ end
+
+ def inet(name, options = {})
+ column(name, 'inet', options)
+ end
+
+ def cidr(name, options = {})
+ column(name, 'cidr', options)
+ end
+
+ def macaddr(name, options = {})
+ column(name, 'macaddr', options)
+ end
+
+ def uuid(name, options = {})
+ column(name, 'uuid', options)
+ end
+
+ def json(name, options = {})
+ column(name, 'json', options)
+ end
+
+ def citext(name, options = {})
+ column(name, 'citext', options)
+ end
+ end
+
+ class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
+ attr_accessor :array
+ end
+
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+ include ColumnMethods
+
+ # Defines the primary key field.
+ # Use of the native PostgreSQL UUID type is supported, and can be used
+ # by defining your tables as such:
+ #
+ # create_table :stuffs, id: :uuid do |t|
+ # t.string :content
+ # t.timestamps
+ # end
+ #
+ # By default, this will use the +uuid_generate_v4()+ function from the
+ # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
+ # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
+ # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
+ # set the +:default+ option to +nil+:
+ #
+ # create_table :stuffs, id: false do |t|
+ # t.primary_key :id, :uuid, default: nil
+ # t.uuid :foo_id
+ # t.timestamps
+ # end
+ #
+ # You may also pass a different UUID generation function from +uuid-ossp+
+ # or another library.
+ #
+ # Note that setting the UUID primary key default value to +nil+ will
+ # require you to assure that you always provide a UUID value before saving
+ # a record (as primary keys cannot be +nil+). This might be done via the
+ # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
+ def primary_key(name, type = :primary_key, options = {})
+ return super unless type == :uuid
+ options[:default] = options.fetch(:default, 'uuid_generate_v4()')
+ options[:primary_key] = true
+ column name, type, options
+ end
+
+ def column(name, type = nil, options = {})
+ super
+ column = self[name]
+ column.array = options[:array]
+
+ self
+ end
+
+ private
+
+ def create_column_definition(name, type)
+ PostgreSQL::ColumnDefinition.new name, type
+ end
+ end
+
+ class Table < ActiveRecord::ConnectionAdapters::Table
+ include ColumnMethods
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index c04a1d7178..484c44dc8d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -5,7 +5,7 @@ module ActiveRecord
private
def visit_AddColumn(o)
- sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
+ sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
add_column_options!(sql, column_options(o))
end
@@ -179,7 +179,9 @@ module ActiveRecord
# Limit, precision, and scale are all handled by the superclass.
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
- PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
+ default_value = extract_value_from_default(default)
+ default_function = extract_default_function(default_value, default)
+ PostgreSQLColumn.new(column_name, default_value, oid, type, notnull == 'f', default_function)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index e3a2422160..027169ae3c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -6,6 +6,7 @@ require 'active_record/connection_adapters/postgresql/column'
require 'active_record/connection_adapters/postgresql/oid'
require 'active_record/connection_adapters/postgresql/quoting'
require 'active_record/connection_adapters/postgresql/referential_integrity'
+require 'active_record/connection_adapters/postgresql/schema_definitions'
require 'active_record/connection_adapters/postgresql/schema_statements'
require 'active_record/connection_adapters/postgresql/database_statements'
@@ -73,139 +74,6 @@ module ActiveRecord
# In addition, default connection parameters of libpq can be set per environment variables.
# See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
class PostgreSQLAdapter < AbstractAdapter
- class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
- attr_accessor :array
- end
-
- module ColumnMethods
- def xml(*args)
- options = args.extract_options!
- column(args[0], 'xml', options)
- end
-
- def tsvector(*args)
- options = args.extract_options!
- column(args[0], 'tsvector', options)
- end
-
- def int4range(name, options = {})
- column(name, 'int4range', options)
- end
-
- def int8range(name, options = {})
- column(name, 'int8range', options)
- end
-
- def tsrange(name, options = {})
- column(name, 'tsrange', options)
- end
-
- def tstzrange(name, options = {})
- column(name, 'tstzrange', options)
- end
-
- def numrange(name, options = {})
- column(name, 'numrange', options)
- end
-
- def daterange(name, options = {})
- column(name, 'daterange', options)
- end
-
- def hstore(name, options = {})
- column(name, 'hstore', options)
- end
-
- def ltree(name, options = {})
- column(name, 'ltree', options)
- end
-
- def inet(name, options = {})
- column(name, 'inet', options)
- end
-
- def cidr(name, options = {})
- column(name, 'cidr', options)
- end
-
- def macaddr(name, options = {})
- column(name, 'macaddr', options)
- end
-
- def uuid(name, options = {})
- column(name, 'uuid', options)
- end
-
- def json(name, options = {})
- column(name, 'json', options)
- end
-
- def citext(name, options = {})
- column(name, 'citext', options)
- end
- end
-
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
- include ColumnMethods
-
- # Defines the primary key field.
- # Use of the native PostgreSQL UUID type is supported, and can be used
- # by defining your tables as such:
- #
- # create_table :stuffs, id: :uuid do |t|
- # t.string :content
- # t.timestamps
- # end
- #
- # By default, this will use the +uuid_generate_v4()+ function from the
- # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
- # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
- # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
- # set the +:default+ option to +nil+:
- #
- # create_table :stuffs, id: false do |t|
- # t.primary_key :id, :uuid, default: nil
- # t.uuid :foo_id
- # t.timestamps
- # end
- #
- # You may also pass a different UUID generation function from +uuid-ossp+
- # or another library.
- #
- # Note that setting the UUID primary key default value to +nil+ will
- # require you to assure that you always provide a UUID value before saving
- # a record (as primary keys cannot be +nil+). This might be done via the
- # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
- def primary_key(name, type = :primary_key, options = {})
- return super unless type == :uuid
- options[:default] = options.fetch(:default, 'uuid_generate_v4()')
- options[:primary_key] = true
- column name, type, options
- end
-
- def citext(name, options = {})
- column(name, 'citext', options)
- end
-
- def column(name, type = nil, options = {})
- super
- column = self[name]
- column.array = options[:array]
-
- self
- end
-
- private
-
- def create_column_definition(name, type)
- ColumnDefinition.new name, type
- end
- end
-
- class Table < ActiveRecord::ConnectionAdapters::Table
- include ColumnMethods
- end
-
ADAPTER_NAME = 'PostgreSQL'
NATIVE_DATABASE_TYPES = {
@@ -251,13 +119,13 @@ module ActiveRecord
ADAPTER_NAME
end
- def schema_creation
+ def schema_creation # :nodoc:
PostgreSQL::SchemaCreation.new self
end
# Adds `:array` option to the default set provided by the
# AbstractAdapter
- def prepare_column_options(column, types)
+ def prepare_column_options(column, types) # :nodoc:
spec = super
spec[:array] = 'true' if column.respond_to?(:array) && column.array
spec[:default] = "\"#{column.default_function}\"" if column.default_function
@@ -509,7 +377,7 @@ module ActiveRecord
end
def update_table_definition(table_name, base) #:nodoc:
- Table.new(table_name, base)
+ PostgreSQL::Table.new(table_name, base)
end
protected
@@ -538,12 +406,12 @@ module ActiveRecord
private
- def get_oid_type(oid, fmod, column_name, sql_type = '')
+ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
if !type_map.key?(oid)
load_additional_types(type_map, [oid])
end
- type_map.fetch(normalize_oid_type(oid, fmod), sql_type) {
+ type_map.fetch(oid, fmod, sql_type) {
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
Type::Value.new.tap do |cast_type|
type_map.register_type(oid, cast_type)
@@ -551,24 +419,7 @@ module ActiveRecord
}
end
- OID_FOR_DECIMAL_TREATED_AS_INT = 23 # :nodoc:
-
- def normalize_oid_type(ftype, fmod)
- # The type for the numeric depends on the width of the field,
- # so we'll do something special here.
- #
- # When dealing with decimal columns:
- #
- # places after decimal = fmod - 4 & 0xffff
- # places before decimal = (fmod - 4) >> 16 & 0xffff
- if ftype == 1700 && (fmod - 4 & 0xffff).zero?
- OID_FOR_DECIMAL_TREATED_AS_INT
- else
- ftype
- end
- end
-
- def initialize_type_map(m)
+ def initialize_type_map(m) # :nodoc:
register_class_with_limit m, 'int2', OID::Integer
m.alias_type 'int4', 'int2'
m.alias_type 'int8', 'int2'
@@ -610,20 +461,29 @@ module ActiveRecord
m.alias_type 'lseg', 'varchar'
m.alias_type 'box', 'varchar'
- m.register_type 'timestamp' do |_, sql_type|
+ m.register_type 'timestamp' do |_, _, sql_type|
precision = extract_precision(sql_type)
OID::DateTime.new(precision: precision)
end
- m.register_type 'numeric' do |_, sql_type|
+ m.register_type 'numeric' do |_, fmod, sql_type|
precision = extract_precision(sql_type)
scale = extract_scale(sql_type)
- OID::Decimal.new(precision: precision, scale: scale)
- end
- m.register_type OID_FOR_DECIMAL_TREATED_AS_INT do |_, sql_type|
- precision = extract_precision(sql_type)
- OID::Integer.new(precision: precision)
+ # The type for the numeric depends on the width of the field,
+ # so we'll do something special here.
+ #
+ # When dealing with decimal columns:
+ #
+ # places after decimal = fmod - 4 & 0xffff
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
+ if fmod && (fmod - 4 & 0xffff).zero?
+ # FIXME: Remove this class, and the second argument to
+ # lookups on PG
+ Type::DecimalWithoutScale.new(precision: precision)
+ else
+ OID::Decimal.new(precision: precision, scale: scale)
+ end
end
load_additional_types(m)
@@ -637,7 +497,78 @@ module ActiveRecord
end
end
- def load_additional_types(type_map, oids = nil)
+ # Extracts the value from a PostgreSQL column default definition.
+ def extract_value_from_default(default) # :nodoc:
+ # This is a performance optimization for Ruby 1.9.2 in development.
+ # If the value is nil, we return nil straight away without checking
+ # the regular expressions. If we check each regular expression,
+ # Regexp#=== will call NilClass#to_str, which will trigger
+ # method_missing (defined by whiny nil in ActiveSupport) which
+ # makes this method very very slow.
+ return default unless default
+
+ case default
+ when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
+ $1
+ # Numeric types
+ when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
+ $1
+ # Character types
+ when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
+ $1.gsub(/''/, "'")
+ # Binary data types
+ when /\A'(.*)'::bytea\z/m
+ $1
+ # Date/time types
+ when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
+ $1
+ when /\A'(.*)'::interval\z/
+ $1
+ # Boolean type
+ when 'true'
+ true
+ when 'false'
+ false
+ # Geometric types
+ when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
+ $1
+ # Network address types
+ when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
+ $1
+ # Bit string types
+ when /\AB'(.*)'::"?bit(?: varying)?"?\z/
+ $1
+ # XML type
+ when /\A'(.*)'::xml\z/m
+ $1
+ # Arrays
+ when /\A'(.*)'::"?\D+"?\[\]\z/
+ $1
+ # Hstore
+ when /\A'(.*)'::hstore\z/
+ $1
+ # JSON
+ when /\A'(.*)'::json\z/
+ $1
+ # Object identifier types
+ when /\A-?\d+\z/
+ $1
+ else
+ # Anything else is blank, some user type, or some function
+ # and we can't know the value of that, so return nil.
+ nil
+ end
+ end
+
+ def extract_default_function(default_value, default) # :nodoc:
+ default if has_default_function?(default_value, default)
+ end
+
+ def has_default_function?(default_value, default) # :nodoc:
+ !default_value && (%r{\w+\(.*\)} === default)
+ end
+
+ def load_additional_types(type_map, oids = nil) # :nodoc:
if supports_ranges?
query = <<-SQL
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
@@ -831,23 +762,13 @@ module ActiveRecord
end_sql
end
- def extract_pg_identifier_from_name(name) # :nodoc:
- match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
-
- if match_data
- rest = name[match_data[0].length, name.length]
- rest = rest[1, rest.length] if rest.start_with? "."
- [match_data[1], (rest.length > 0 ? rest : nil)]
- end
- end
-
def extract_table_ref_from_insert_sql(sql) # :nodoc:
sql[/into\s+([^\(]*).*values\s*\(/im]
$1.strip if $1
end
def create_table_definition(name, temporary, options, as = nil) # :nodoc:
- TableDefinition.new native_database_types, name, temporary, options, as
+ PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index e5c9f6f54a..4d8afcf16a 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -12,11 +12,10 @@ module ActiveRecord
@columns_hash = {}
@primary_keys = {}
@tables = {}
- prepare_default_proc
end
def primary_keys(table_name)
- @primary_keys[table_name]
+ @primary_keys[table_name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil
end
# A cached lookup for table existence.
@@ -29,9 +28,9 @@ module ActiveRecord
# Add internal cache for table with +table_name+.
def add(table_name)
if table_exists?(table_name)
- @primary_keys[table_name]
- @columns[table_name]
- @columns_hash[table_name]
+ primary_keys(table_name)
+ columns(table_name)
+ columns_hash(table_name)
end
end
@@ -40,14 +39,16 @@ module ActiveRecord
end
# Get the columns for a table
- def columns(table)
- @columns[table]
+ def columns(table_name)
+ @columns[table_name] ||= connection.columns(table_name)
end
# Get the columns for a table as a hash, key is the column name
# value is the column object.
- def columns_hash(table)
- @columns_hash[table]
+ def columns_hash(table_name)
+ @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
+ [col.name, col]
+ }]
end
# Clears out internal caches
@@ -76,32 +77,11 @@ module ActiveRecord
def marshal_dump
# if we get current version during initialization, it happens stack over flow.
@version = ActiveRecord::Migrator.current_version
- [@version] + [@columns, @columns_hash, @primary_keys, @tables].map { |val|
- Hash[val]
- }
+ [@version, @columns, @columns_hash, @primary_keys, @tables]
end
def marshal_load(array)
@version, @columns, @columns_hash, @primary_keys, @tables = array
- prepare_default_proc
- end
-
- private
-
- def prepare_default_proc
- @columns.default_proc = Proc.new do |h, table_name|
- h[table_name] = connection.columns(table_name)
- end
-
- @columns_hash.default_proc = Proc.new do |h, table_name|
- h[table_name] = Hash[columns(table_name).map { |col|
- [col.name, col]
- }]
- end
-
- @primary_keys.default_proc = Proc.new do |h, table_name|
- h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/type.rb b/activerecord/lib/active_record/connection_adapters/type.rb
index 395a4160a8..bab7a3ff7e 100644
--- a/activerecord/lib/active_record/connection_adapters/type.rb
+++ b/activerecord/lib/active_record/connection_adapters/type.rb
@@ -7,6 +7,7 @@ require 'active_record/connection_adapters/type/boolean'
require 'active_record/connection_adapters/type/date'
require 'active_record/connection_adapters/type/date_time'
require 'active_record/connection_adapters/type/decimal'
+require 'active_record/connection_adapters/type/decimal_without_scale'
require 'active_record/connection_adapters/type/float'
require 'active_record/connection_adapters/type/integer'
require 'active_record/connection_adapters/type/string'
diff --git a/activerecord/lib/active_record/connection_adapters/type/binary.rb b/activerecord/lib/active_record/connection_adapters/type/binary.rb
index 4b2d1a66e0..60afe44de1 100644
--- a/activerecord/lib/active_record/connection_adapters/type/binary.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/binary.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Binary < Value # :nodoc:
+ class Binary < Value
def type
:binary
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/boolean.rb b/activerecord/lib/active_record/connection_adapters/type/boolean.rb
index 2337bdd563..0d97379189 100644
--- a/activerecord/lib/active_record/connection_adapters/type/boolean.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/boolean.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Boolean < Value # :nodoc:
+ class Boolean < Value
def type
:boolean
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/date.rb b/activerecord/lib/active_record/connection_adapters/type/date.rb
index 1e7205fd0b..e8becbe1f4 100644
--- a/activerecord/lib/active_record/connection_adapters/type/date.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/date.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Date < Value # :nodoc:
+ class Date < Value
def type
:date
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/date_time.rb b/activerecord/lib/active_record/connection_adapters/type/date_time.rb
index c34f4c5a53..64f5d05301 100644
--- a/activerecord/lib/active_record/connection_adapters/type/date_time.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/date_time.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class DateTime < Value # :nodoc:
+ class DateTime < Value
include TimeValue
def type
diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal.rb b/activerecord/lib/active_record/connection_adapters/type/decimal.rb
index ac5af4b963..e93906ba19 100644
--- a/activerecord/lib/active_record/connection_adapters/type/decimal.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/decimal.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Decimal < Value # :nodoc:
+ class Decimal < Value
include Numeric
def type
diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb b/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb
new file mode 100644
index 0000000000..e58c6e198d
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/decimal_without_scale.rb
@@ -0,0 +1,13 @@
+require 'active_record/connection_adapters/type/integer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class DecimalWithoutScale < Integer # :nodoc:
+ def type
+ :decimal
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/float.rb b/activerecord/lib/active_record/connection_adapters/type/float.rb
index 51cfa5d86a..f2427d2dfa 100644
--- a/activerecord/lib/active_record/connection_adapters/type/float.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/float.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Float < Value # :nodoc:
+ class Float < Value
include Numeric
def type
@@ -12,6 +12,8 @@ module ActiveRecord
::Float
end
+ alias type_cast_for_database type_cast
+
private
def cast_value(value)
diff --git a/activerecord/lib/active_record/connection_adapters/type/integer.rb b/activerecord/lib/active_record/connection_adapters/type/integer.rb
index 8f3469434c..596f4de2a8 100644
--- a/activerecord/lib/active_record/connection_adapters/type/integer.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/integer.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Integer < Value # :nodoc:
+ class Integer < Value
include Numeric
def type
@@ -12,6 +12,8 @@ module ActiveRecord
::Fixnum
end
+ alias type_cast_for_database type_cast
+
private
def cast_value(value)
diff --git a/activerecord/lib/active_record/connection_adapters/type/string.rb b/activerecord/lib/active_record/connection_adapters/type/string.rb
index 55f0e1ee1c..471f949e09 100644
--- a/activerecord/lib/active_record/connection_adapters/type/string.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/string.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class String < Value # :nodoc:
+ class String < Value
def type
:string
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/text.rb b/activerecord/lib/active_record/connection_adapters/type/text.rb
index ee5842a3fc..61095ebb38 100644
--- a/activerecord/lib/active_record/connection_adapters/type/text.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/text.rb
@@ -3,7 +3,7 @@ require 'active_record/connection_adapters/type/string'
module ActiveRecord
module ConnectionAdapters
module Type
- class Text < String # :nodoc:
+ class Text < String
def type
:text
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/time.rb b/activerecord/lib/active_record/connection_adapters/type/time.rb
index 4dd201e3fe..bc331b0fa7 100644
--- a/activerecord/lib/active_record/connection_adapters/type/time.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/time.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Time < Value # :nodoc:
+ class Time < Value
include TimeValue
def type
diff --git a/activerecord/lib/active_record/connection_adapters/type/value.rb b/activerecord/lib/active_record/connection_adapters/type/value.rb
index 54a3e9dd7a..60b443004c 100644
--- a/activerecord/lib/active_record/connection_adapters/type/value.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/value.rb
@@ -1,9 +1,11 @@
module ActiveRecord
module ConnectionAdapters
module Type
- class Value # :nodoc:
+ class Value
attr_reader :precision, :scale, :limit
+ # Valid options are +precision+, +scale+, and +limit+.
+ # They are only used when dumping schema.
def initialize(options = {})
options.assert_valid_keys(:precision, :scale, :limit)
@precision = options[:precision]
@@ -11,8 +13,13 @@ module ActiveRecord
@limit = options[:limit]
end
+ # The simplified that this object represents. Subclasses
+ # should override this method.
def type; end
+ # Takes an input from the database, or from attribute setters,
+ # and casts it to a type appropriate for this object. This method
+ # should not be overriden by subclasses. Instead, override `cast_value`.
def type_cast(value)
cast_value(value) unless value.nil?
end
@@ -21,6 +28,10 @@ module ActiveRecord
value
end
+ def type_cast_for_database(value)
+ type_cast_for_write(value)
+ end
+
def text?
false
end
@@ -39,7 +50,9 @@ module ActiveRecord
private
- def cast_value(value)
+ # Responsible for casting values from external sources to the appropriate
+ # type. Called by `type_cast` for all values except `nil`.
+ def cast_value(value) # :api: public
value
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 4571cc0786..07eafef788 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -286,6 +286,8 @@ module ActiveRecord
@new_record = false
+ self.class.define_attribute_methods
+
run_callbacks :find
run_callbacks :initialize
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 71e176a328..05c4b13016 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -20,7 +20,7 @@ module ActiveRecord
def reset_counters(id, *counters)
object = find(id)
counters.each do |counter_association|
- has_many_association = reflect_on_association(counter_association.to_sym)
+ has_many_association = _reflect_on_association(counter_association.to_sym)
unless has_many_association
has_many = reflect_on_all_associations(:has_many)
has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
@@ -34,8 +34,7 @@ module ActiveRecord
foreign_key = has_many_association.foreign_key.to_s
child_class = has_many_association.klass
- belongs_to = child_class.reflect_on_all_associations(:belongs_to)
- reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
+ reflection = child_class._reflections.values.find { |e| :belongs_to == e.macro && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
counter_name = reflection.counter_cache_column
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
@@ -167,7 +166,7 @@ module ActiveRecord
end
def each_counter_cached_associations
- reflections.each do |name, reflection|
+ _reflections.each do |name, reflection|
yield association(name) if reflection.belongs_to? && reflection.counter_cache_column
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 47d32fae05..d40bea5ea7 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -649,7 +649,7 @@ module ActiveRecord
model_class
end
- reflection_class.reflect_on_all_associations.each do |association|
+ reflection_class._reflections.values.each do |association|
case association.macro
when :belongs_to
# Do not replace association name with association foreign key if they are named the same
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index aa1166750f..a4e10ed2e7 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -217,20 +217,6 @@ module ActiveRecord
connection.schema_cache.table_exists?(table_name)
end
- # Returns an array of column objects for the table associated with this class.
- def columns
- @columns ||= connection.schema_cache.columns(table_name).map do |col|
- col = col.dup
- col.primary = (col.name == primary_key)
- col
- end
- end
-
- # Returns a hash of column objects for the table associated with this class.
- def columns_hash
- @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
- end
-
def column_types # :nodoc:
@column_types ||= decorate_columns(columns_hash.dup)
end
@@ -271,7 +257,7 @@ module ActiveRecord
# Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
# and columns used for single table inheritance have been removed.
def content_columns
- @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
+ @content_columns ||= columns.reject { |c| c.name == primary_key || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
end
# Resets all the cached information about columns, which will cause them
@@ -308,8 +294,6 @@ module ActiveRecord
@arel_engine = nil
@column_defaults = nil
@column_names = nil
- @columns = nil
- @columns_hash = nil
@column_types = nil
@content_columns = nil
@dynamic_methods_hash = nil
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 29ed499b1b..7dc7169a02 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -305,7 +305,7 @@ module ActiveRecord
options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
attr_names.each do |association_name|
- if reflection = reflect_on_association(association_name)
+ if reflection = _reflect_on_association(association_name)
reflection.autosave = true
add_autosave_association_callbacks(reflection)
@@ -542,7 +542,7 @@ module ActiveRecord
end
def raise_nested_attributes_record_not_found!(association_name, record_id)
- raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
+ raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
end
end
end
diff --git a/activerecord/lib/active_record/properties.rb b/activerecord/lib/active_record/properties.rb
new file mode 100644
index 0000000000..39c39ad9ff
--- /dev/null
+++ b/activerecord/lib/active_record/properties.rb
@@ -0,0 +1,107 @@
+module ActiveRecord
+ module Properties
+ extend ActiveSupport::Concern
+
+ Type = ConnectionAdapters::Type
+
+ module ClassMethods
+ # Defines or overrides a property on this model. This allows customization of
+ # Active Record's type casting behavior, as well as adding support for user defined
+ # types.
+ #
+ # ==== Examples
+ #
+ # The type detected by Active Record can be overriden.
+ #
+ # # db/schema.rb
+ # create_table :store_listings, force: true do |t|
+ # t.decimal :price_in_cents
+ # end
+ #
+ # # app/models/store_listing.rb
+ # class StoreListing < ActiveRecord::Base
+ # end
+ #
+ # store_listing = StoreListing.new(price_in_cents: '10.1')
+ #
+ # # before
+ # store_listing.price_in_cents # => BigDecimal.new(10.1)
+ #
+ # class StoreListing < ActiveRecord::Base
+ # property :price_in_cents, Type::Integer.new
+ # end
+ #
+ # # after
+ # store_listing.price_in_cents # => 10
+ #
+ # Users may also define their own custom types, as long as they respond to the methods
+ # defined on the value type. The `type_cast` method on your type object will be called
+ # with values both from the database, and from your controllers. See
+ # `ActiveRecord::Properties::Type::Value` for the expected API. It is recommended that your
+ # type objects inherit from an existing type, or the base value type.
+ #
+ # class MoneyType < ActiveRecord::Type::Integer
+ # def type_cast(value)
+ # if value.include?('$')
+ # price_in_dollars = value.gsub(/\$/, '').to_f
+ # price_in_dollars * 100
+ # else
+ # value.to_i
+ # end
+ # end
+ # end
+ #
+ # class StoreListing < ActiveRecord::Base
+ # property :price_in_cents, MoneyType.new
+ # end
+ #
+ # store_listing = StoreListing.new(price_in_cents: '$10.00')
+ # store_listing.price_in_cents # => 1000
+ def property(name, cast_type)
+ name = name.to_s
+ user_provided_columns[name] = ConnectionAdapters::Column.new(name, nil, cast_type)
+ end
+
+ # Returns an array of column objects for the table associated with this class.
+ def columns
+ @columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name)).each do |column|
+ if Type::DecimalWithoutScale === column.cast_type
+ ActiveSupport::Deprecation.warn <<-MESSAGE.strip_heredoc
+ Decimal columns with 0 scale being automatically treated as integers
+ is deprecated, and will be removed in a future version of Rails. If
+ you'd like to keep this behavior, add
+
+ property :#{column.name}, Type::Integer.new
+
+ to your #{name} model.
+ MESSAGE
+ end
+ end
+ end
+
+ # Returns a hash of column objects for the table associated with this class.
+ def columns_hash
+ @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
+ end
+
+ def reset_column_information # :nodoc:
+ super
+
+ @columns = nil
+ @columns_hash = nil
+ end
+
+ private
+
+ def user_provided_columns
+ @user_provided_columns ||= {}
+ end
+
+ def add_user_provided_columns(schema_columns)
+ schema_columns.reject { |column|
+ user_provided_columns.key? column.name
+ } + user_provided_columns.values
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 0eec6774a0..dd80ec6274 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -6,9 +6,9 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :reflections
+ class_attribute :_reflections
class_attribute :aggregate_reflections
- self.reflections = {}
+ self._reflections = {}
self.aggregate_reflections = {}
end
@@ -24,7 +24,7 @@ module ActiveRecord
end
def self.add_reflection(ar, name, reflection)
- ar.reflections = ar.reflections.merge(name.to_s => reflection)
+ ar._reflections = ar._reflections.merge(name.to_s => reflection)
end
def self.add_aggregate_reflection(ar, name, reflection)
@@ -53,6 +53,24 @@ module ActiveRecord
aggregate_reflections[aggregation.to_s]
end
+ # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
+ #
+ # Account.reflections # => {balance: AggregateReflection}
+ #
+ # @api public
+ def reflections
+ ref = {}
+ _reflections.each do |name, reflection|
+ parent_name, parent_reflection = reflection.parent_reflection
+ if parent_name
+ ref[parent_name] = parent_reflection
+ else
+ ref[name] = reflection
+ end
+ end
+ ref
+ end
+
# Returns an array of AssociationReflection objects for all the
# associations in the class. If you only want to reflect on a certain
# association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
@@ -63,6 +81,7 @@ module ActiveRecord
# Account.reflect_on_all_associations # returns an array of all associations
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
#
+ # @api public
def reflect_on_all_associations(macro = nil)
association_reflections = reflections.values
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
@@ -73,11 +92,19 @@ module ActiveRecord
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
#
+ # @api public
def reflect_on_association(association)
reflections[association.to_s]
end
+ # @api private
+ def _reflect_on_association(association) #:nodoc:
+ _reflections[association.to_s]
+ end
+
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
+ #
+ # @api public
def reflect_on_all_autosave_associations
reflections.values.select { |reflection| reflection.options[:autosave] }
end
@@ -129,6 +156,10 @@ module ActiveRecord
def autosave=(autosave)
@automatic_inverse_of = false
@options[:autosave] = autosave
+ _, parent_reflection = self.parent_reflection
+ if parent_reflection
+ parent_reflection.autosave = autosave
+ end
end
# Returns the class for the macro.
@@ -193,10 +224,11 @@ module ActiveRecord
end
attr_reader :type, :foreign_type
+ attr_accessor :parent_reflection # [:name, Reflection]
def initialize(macro, name, scope, options, active_record)
super
- @collection = :has_many == macro
+ @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
@automatic_inverse_of = nil
@type = options[:as] && "#{options[:as]}_type"
@foreign_type = options[:foreign_type] || "#{name}_type"
@@ -330,12 +362,12 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
def inverse_of
return unless inverse_name
- @inverse_of ||= klass.reflect_on_association inverse_name
+ @inverse_of ||= klass._reflect_on_association inverse_name
end
def polymorphic_inverse_of(associated_class)
if has_inverse?
- if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
+ if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
inverse_relationship
else
raise InverseOfAssociationNotFoundError.new(self, associated_class)
@@ -436,7 +468,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
inverse_name = ActiveSupport::Inflector.underscore(active_record.name).to_sym
begin
- reflection = klass.reflect_on_association(inverse_name)
+ reflection = klass._reflect_on_association(inverse_name)
rescue NameError
# Give up: we couldn't compute the klass type so we won't be able
# to find any associations either.
@@ -535,7 +567,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
#
def source_reflection
- through_reflection.klass.reflect_on_association(source_reflection_name)
+ through_reflection.klass._reflect_on_association(source_reflection_name)
end
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
@@ -551,7 +583,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @active_record=Post, @plural_name="taggings">
#
def through_reflection
- active_record.reflect_on_association(options[:through])
+ active_record._reflect_on_association(options[:through])
end
# Returns an array of reflections which are involved in this association. Each item in the
@@ -658,7 +690,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
names = names.find_all { |n|
- through_reflection.klass.reflect_on_association(n)
+ through_reflection.klass._reflect_on_association(n)
}
if names.length > 1
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 56cf9bcd27..d155517b18 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -277,7 +277,7 @@ module ActiveRecord
group_attrs = group_values
if group_attrs.first.respond_to?(:to_sym)
- association = @klass.reflect_on_association(group_attrs.first.to_sym)
+ association = @klass._reflect_on_association(group_attrs.first.to_sym)
associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
group_fields = Array(associated ? association.foreign_key : group_attrs)
else
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index db32ae12a8..47e90e9021 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -336,7 +336,16 @@ module ActiveRecord
end
def find_with_associations
- join_dependency = construct_join_dependency
+ # NOTE: the JoinDependency constructed here needs to know about
+ # any joins already present in `self`, so pass them in
+ #
+ # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
+ # incorrect SQL is generated. In that case, the join dependency for
+ # SpecialCategorizations is constructed without knowledge of the
+ # preexisting join in joins_values to categorizations (by way of
+ # the `has_many :through` for categories).
+ #
+ join_dependency = construct_join_dependency(joins_values)
aliases = join_dependency.aliases
relation = select aliases.columns
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index d40f276968..eff5c8f09c 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -26,7 +26,7 @@ module ActiveRecord
queries << '1=0'
else
table = Arel::Table.new(column, default_table.engine)
- association = klass.reflect_on_association(column.to_sym)
+ association = klass._reflect_on_association(column.to_sym)
value.each do |k, v|
queries.concat expand(association && association.klass, table, k, v)
@@ -55,7 +55,7 @@ module ActiveRecord
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(estimate_of: treasure)
- if klass && reflection = klass.reflect_on_association(column.to_sym)
+ if klass && reflection = klass._reflect_on_association(column.to_sym)
if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
queries << build(table[reflection.foreign_type], base_class)
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index 2f6c34ac08..78dba8be06 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -2,28 +2,33 @@ module ActiveRecord
class PredicateBuilder
class ArrayHandler # :nodoc:
def call(attribute, value)
+ return attribute.in([]) if value.empty?
+
values = value.map { |x| x.is_a?(Base) ? x.id : x }
ranges, values = values.partition { |v| v.is_a?(Range) }
+ nils, values = values.partition(&:nil?)
- values_predicate = if values.include?(nil)
- values = values.compact
-
+ values_predicate =
case values.length
- when 0
- attribute.eq(nil)
- when 1
- attribute.eq(values.first).or(attribute.eq(nil))
- else
- attribute.in(values).or(attribute.eq(nil))
+ when 0 then NullPredicate
+ when 1 then attribute.eq(values.first)
+ else attribute.in(values)
end
- else
- attribute.in(values)
+
+ unless nils.empty?
+ values_predicate = values_predicate.or(attribute.eq(nil))
end
array_predicates = ranges.map { |range| attribute.in(range) }
array_predicates << values_predicate
array_predicates.inject { |composite, predicate| composite.or(predicate) }
end
+
+ module NullPredicate
+ def self.or(other)
+ other
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index a607e9ac87..1262b2c291 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -573,15 +573,11 @@ WARNING
end
end
- def where!(opts = :chain, *rest) # :nodoc:
- if opts == :chain
- WhereChain.new(self)
- else
- references!(PredicateBuilder.references(opts)) if Hash === opts
+ def where!(opts, *rest) # :nodoc:
+ references!(PredicateBuilder.references(opts)) if Hash === opts
- self.where_values += build_where(opts, rest)
- self
- end
+ self.where_values += build_where(opts, rest)
+ self
end
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb
index 9a19483da3..e586744818 100644
--- a/activerecord/lib/active_record/validations/presence.rb
+++ b/activerecord/lib/active_record/validations/presence.rb
@@ -4,7 +4,7 @@ module ActiveRecord
def validate(record)
super
attributes.each do |attribute|
- next unless record.class.reflect_on_association(attribute)
+ next unless record.class._reflect_on_association(attribute)
associated_records = Array.wrap(record.send(attribute))
# Superclass validates presence. Ensure present records aren't about to be destroyed.
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index ee080451a9..b6fccc9b94 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -47,7 +47,7 @@ module ActiveRecord
end
def build_relation(klass, table, attribute, value) #:nodoc:
- if reflection = klass.reflect_on_association(attribute)
+ if reflection = klass._reflect_on_association(attribute)
attribute = reflection.foreign_key
value = value.attributes[reflection.primary_key_column.name] unless value.nil?
end
@@ -74,7 +74,7 @@ module ActiveRecord
def scope_relation(record, table, relation)
Array(options[:scope]).each do |scope_item|
- if reflection = record.class.reflect_on_association(scope_item)
+ if reflection = record.class._reflect_on_association(scope_item)
scope_value = record.send(reflection.foreign_key)
scope_item = reflection.foreign_key
else
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index 1f55cce352..972abf7cdc 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -122,7 +122,6 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase
assert_equal "Champs-Élysées", composite.address.street
composite.address = FullAddress.new("Paris", "Rue Basse")
- skip "Saving with custom OID type is currently not supported."
composite.save!
assert_equal 'Paris', composite.reload.address.city
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index b9e296ed8f..b6c6e38f62 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -129,7 +129,7 @@ class SchemaTest < ActiveRecord::TestCase
SQL
song = Song.create
- album = Album.create
+ Album.create
assert_equal song, Song.includes(:albums).references(:albums).first
ensure
ActiveRecord::Base.connection.execute "DROP SCHEMA music CASCADE;"
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index b478db749d..13b754d226 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -60,7 +60,6 @@ class CopyTableTest < ActiveRecord::TestCase
assert_equal original_id.type, copied_id.type
assert_equal original_id.sql_type, copied_id.sql_type
assert_equal original_id.limit, copied_id.limit
- assert_equal original_id.primary, copied_id.primary
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 0c4f06d6a9..8c9a051eea 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -15,10 +15,10 @@ module ActiveRecord
def test_type_cast_binary_encoding_without_logger
@conn.extend(Module.new { def logger; end })
- column = Struct.new(:type, :name).new(:string, "foo")
+ cast_type = Type::String.new
binary = SecureRandom.hex
expected = binary.dup.encode!(Encoding::UTF_8)
- assert_equal expected, @conn.type_cast(binary, column)
+ assert_equal expected, @conn.type_cast(binary, cast_type)
end
def test_type_cast_symbol
@@ -84,7 +84,7 @@ module ActiveRecord
assert_raise(TypeError) { @conn.type_cast(obj, nil) }
end
- def test_quoted_id
+ def test_type_cast_object_which_responds_to_quoted_id
quoted_id_obj = Class.new {
def quoted_id
"'zomg'"
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index ebcf453305..4bd4486b41 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1167,6 +1167,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
)
end
+ test "deep preload" do
+ post = Post.preload(author: :posts, comments: :post).first
+
+ assert_predicate post.author.association(:posts), :loaded?
+ assert_predicate post.comments.first.association(:post), :loaded?
+ end
+
test "preloading does not cache has many association subset when preloaded with a through association" do
author = Author.includes(:comments_with_order_and_conditions, :posts).first
assert_no_queries { assert_equal 2, author.comments_with_order_and_conditions.size }
@@ -1196,6 +1203,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
+ test "preloading the same association twice works" do
+ Member.create!
+ members = Member.preload(:current_membership).includes(current_membership: :club).all.to_a
+ assert_no_queries {
+ members_with_membership = members.select(&:current_membership)
+ assert_equal 3, members_with_membership.map(&:current_membership).map(&:club).size
+ }
+ end
+
test "preloading with a polymorphic association and using the existential predicate" do
assert_equal authors(:david), authors(:david).essays.includes(:writer).first.writer
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 878f1877db..8d8201ddae 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
@@ -70,6 +70,14 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base
:foreign_key => "developer_id"
end
+class SubDeveloper < Developer
+ self.table_name = 'developers'
+ has_and_belongs_to_many :special_projects,
+ :join_table => 'developers_projects',
+ :foreign_key => "project_id",
+ :association_foreign_key => "developer_id"
+end
+
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings
@@ -814,7 +822,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal [], Pirate.where(id: redbeard.id)
end
- test "has and belongs to many associations on new records use null relations" do
+ def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations
projects = Developer.new.projects
assert_no_queries do
assert_equal [], projects
@@ -860,4 +868,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_includes magazine.articles, article
end
+
+ def test_redefine_habtm
+ child = SubDeveloper.new("name" => "Aredridel")
+ child.special_projects << SpecialProject.new("name" => "Special Project")
+ assert_equal true, child.save
+ end
end
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index b23517b2f9..07cf65a760 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -126,4 +126,14 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
categories = author.categories.includes(:special_categorizations).references(:special_categorizations).to_a
assert_equal 2, categories.size
end
+
+ test "the correct records are loaded when including an aliased association" do
+ author = Author.create! name: "Jon"
+ author.categories.create! name: 'Not Special'
+ author.special_categories.create! name: 'Special'
+
+ categories = author.categories.eager_load(:special_categorizations).order(:name).to_a
+ assert_equal 0, categories.first.special_categorizations.size
+ assert_equal 1, categories.second.special_categorizations.size
+ end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 7c7c1fbfbd..d65c4b0638 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -102,8 +102,8 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_columns_should_obey_set_primary_key
- pk = Subscriber.columns.find { |x| x.name == 'nick' }
- assert pk.primary, 'nick should be primary key'
+ pk = Subscriber.columns_hash[Subscriber.primary_key]
+ assert_equal 'nick', pk.name, 'nick should be primary key'
end
def test_primary_key_with_no_id
@@ -518,6 +518,10 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal Topic.find('1-meowmeow'), Topic.find(1)
end
+ def test_find_by_slug_with_array
+ assert_equal Topic.find(['1-meowmeow', '2-hello']), Topic.find([1, 2])
+ end
+
def test_equality_of_new_records
assert_not_equal Topic.new, Topic.new
assert_equal false, Topic.new == Topic.new
@@ -980,6 +984,10 @@ class BasicsTest < ActiveRecord::TestCase
class NumericData < ActiveRecord::Base
self.table_name = 'numeric_data'
+
+ property :world_population, Type::Integer.new
+ property :my_house_population, Type::Integer.new
+ property :atoms_in_universe, Type::Integer.new
end
def test_big_decimal_conditions
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index 40f73cd68c..0bc7ee6d64 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -21,7 +21,7 @@ module ActiveRecord
super
@connection = ActiveRecord::Base.connection
@subscriber = LogListener.new
- @pk = Topic.columns.find { |c| c.primary }
+ @pk = Topic.columns_hash[Topic.primary_key]
@subscription = ActiveSupport::Notifications.subscribe('sql.active_record', @subscriber)
end
@@ -60,12 +60,10 @@ module ActiveRecord
end
def test_logs_bind_vars
- pk = Topic.columns.find { |x| x.primary }
-
payload = {
:name => 'SQL',
:sql => 'select * from topics where id = ?',
- :binds => [[pk, 10]]
+ :binds => [[@pk, 10]]
}
event = ActiveSupport::Notifications::Event.new(
'foo',
@@ -87,7 +85,7 @@ module ActiveRecord
}.new
logger.sql event
- assert_match([[pk.name, 10]].inspect, logger.debugs.first)
+ assert_match([[@pk.name, 10]].inspect, logger.debugs.first)
end
end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index b8de78934e..b9445ee072 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -15,6 +15,10 @@ Company.has_many :accounts
class NumericData < ActiveRecord::Base
self.table_name = 'numeric_data'
+
+ property :world_population, Type::Integer.new
+ property :my_house_population, Type::Integer.new
+ property :atoms_in_universe, Type::Integer.new
end
class CalculationsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
deleted file mode 100644
index 3257f5bed8..0000000000
--- a/activerecord/test/cases/column_test.rb
+++ /dev/null
@@ -1,159 +0,0 @@
-require "cases/helper"
-require 'models/company'
-
-module ActiveRecord
- module ConnectionAdapters
- class ColumnTest < ActiveRecord::TestCase
- def test_type_cast_boolean
- column = Column.new("field", nil, Type::Boolean.new)
- assert column.type_cast('').nil?
- assert column.type_cast(nil).nil?
-
- assert column.type_cast(true)
- assert column.type_cast(1)
- assert column.type_cast('1')
- assert column.type_cast('t')
- assert column.type_cast('T')
- assert column.type_cast('true')
- assert column.type_cast('TRUE')
- assert column.type_cast('on')
- assert column.type_cast('ON')
-
- # explicitly check for false vs nil
- assert_equal false, column.type_cast(false)
- assert_equal false, column.type_cast(0)
- assert_equal false, column.type_cast('0')
- assert_equal false, column.type_cast('f')
- assert_equal false, column.type_cast('F')
- assert_equal false, column.type_cast('false')
- assert_equal false, column.type_cast('FALSE')
- assert_equal false, column.type_cast('off')
- assert_equal false, column.type_cast('OFF')
- assert_equal false, column.type_cast(' ')
- assert_equal false, column.type_cast("\u3000\r\n")
- assert_equal false, column.type_cast("\u0000")
- assert_equal false, column.type_cast('SOMETHING RANDOM')
- end
-
- def test_type_cast_string
- column = Column.new("field", nil, Type::String.new)
- assert_equal "1", column.type_cast(true)
- assert_equal "0", column.type_cast(false)
- assert_equal "123", column.type_cast(123)
- end
-
- def test_type_cast_integer
- column = Column.new("field", nil, Type::Integer.new)
- assert_equal 1, column.type_cast(1)
- assert_equal 1, column.type_cast('1')
- assert_equal 1, column.type_cast('1ignore')
- assert_equal 0, column.type_cast('bad1')
- assert_equal 0, column.type_cast('bad')
- assert_equal 1, column.type_cast(1.7)
- assert_equal 0, column.type_cast(false)
- assert_equal 1, column.type_cast(true)
- assert_nil column.type_cast(nil)
- end
-
- def test_type_cast_non_integer_to_integer
- column = Column.new("field", nil, Type::Integer.new)
- assert_nil column.type_cast([1,2])
- assert_nil column.type_cast({1 => 2})
- assert_nil column.type_cast((1..2))
- end
-
- def test_type_cast_activerecord_to_integer
- column = Column.new("field", nil, Type::Integer.new)
- firm = Firm.create(:name => 'Apple')
- assert_nil column.type_cast(firm)
- end
-
- def test_type_cast_object_without_to_i_to_integer
- column = Column.new("field", nil, Type::Integer.new)
- assert_nil column.type_cast(Object.new)
- end
-
- def test_type_cast_nan_and_infinity_to_integer
- column = Column.new("field", nil, Type::Integer.new)
- assert_nil column.type_cast(Float::NAN)
- assert_nil column.type_cast(1.0/0.0)
- end
-
- def test_type_cast_float
- column = Column.new("field", nil, Type::Float.new)
- assert_equal 1.0, column.type_cast("1")
- end
-
- def test_type_cast_decimal
- column = Column.new("field", nil, Type::Decimal.new)
- assert_equal BigDecimal.new("0"), column.type_cast(BigDecimal.new("0"))
- assert_equal BigDecimal.new("123"), column.type_cast(123.0)
- assert_equal BigDecimal.new("1"), column.type_cast(:"1")
- end
-
- def test_type_cast_binary
- column = Column.new("field", nil, Type::Binary.new)
- assert_equal nil, column.type_cast(nil)
- assert_equal "1", column.type_cast("1")
- assert_equal 1, column.type_cast(1)
- end
-
- def test_type_cast_time
- column = Column.new("field", nil, Type::Time.new)
- assert_equal nil, column.type_cast(nil)
- assert_equal nil, column.type_cast('')
- assert_equal nil, column.type_cast('ABC')
-
- time_string = Time.now.utc.strftime("%T")
- assert_equal time_string, column.type_cast(time_string).strftime("%T")
- end
-
- def test_type_cast_datetime_and_timestamp
- column = Column.new("field", nil, Type::DateTime.new)
- assert_equal nil, column.type_cast(nil)
- assert_equal nil, column.type_cast('')
- assert_equal nil, column.type_cast(' ')
- assert_equal nil, column.type_cast('ABC')
-
- datetime_string = Time.now.utc.strftime("%FT%T")
- assert_equal datetime_string, column.type_cast(datetime_string).strftime("%FT%T")
- end
-
- def test_type_cast_date
- column = Column.new("field", nil, Type::Date.new)
- assert_equal nil, column.type_cast(nil)
- assert_equal nil, column.type_cast('')
- assert_equal nil, column.type_cast(' ')
- assert_equal nil, column.type_cast('ABC')
-
- date_string = Time.now.utc.strftime("%F")
- assert_equal date_string, column.type_cast(date_string).strftime("%F")
- end
-
- def test_type_cast_duration_to_integer
- column = Column.new("field", nil, Type::Integer.new)
- assert_equal 1800, column.type_cast(30.minutes)
- assert_equal 7200, column.type_cast(2.hours)
- end
-
- def test_string_to_time_with_timezone
- [:utc, :local].each do |zone|
- with_timezone_config default: zone do
- column = Column.new("field", nil, Type::DateTime.new)
- assert_equal Time.utc(2013, 9, 4, 0, 0, 0), column.type_cast("Wed, 04 Sep 2013 03:00:00 EAT")
- end
- end
- end
-
- if current_adapter?(:SQLite3Adapter)
- def test_binary_encoding
- column = Column.new("field", nil, SQLite3Binary.new)
- utf8_string = "a string".encode(Encoding::UTF_8)
- type_cast = column.type_cast(utf8_string)
-
- assert_equal Encoding::ASCII_8BIT, type_cast.encoding
- end
- end
- end
- end
-end
diff --git a/activerecord/test/cases/connection_adapters/type/type_map_test.rb b/activerecord/test/cases/connection_adapters/type/type_map_test.rb
index 3abd7a276e..e85bbc8dc7 100644
--- a/activerecord/test/cases/connection_adapters/type/type_map_test.rb
+++ b/activerecord/test/cases/connection_adapters/type/type_map_test.rb
@@ -125,6 +125,16 @@ module ActiveRecord
assert_equal mapping.lookup(3), 'string'
assert_kind_of Type::Value, mapping.lookup(4)
end
+
+ def test_clear_mappings
+ time = Time.new
+ mapping = TypeMap.new
+
+ mapping.register_type(/time/i, time)
+ mapping.clear
+
+ assert_not_equal mapping.lookup('time'), time
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 18df30faf5..3958c3bfff 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -77,12 +77,16 @@ module ActiveRecord
assert_lookup_type :integer, 'tinyint'
assert_lookup_type :integer, 'smallint'
assert_lookup_type :integer, 'bigint'
- assert_lookup_type :integer, 'decimal(2)'
- assert_lookup_type :integer, 'decimal(2,0)'
- assert_lookup_type :integer, 'numeric(2)'
- assert_lookup_type :integer, 'numeric(2,0)'
- assert_lookup_type :integer, 'number(2)'
- assert_lookup_type :integer, 'number(2,0)'
+ end
+
+ def test_decimal_without_scale
+ types = %w{decimal(2) decimal(2,0) numeric(2) numeric(2,0) number(2) number(2,0)}
+ types.each do |type|
+ cast_type = @connection.type_map.lookup(type)
+
+ assert_equal :decimal, cast_type.type
+ assert_equal 2, cast_type.type_cast(2.1)
+ end
end
private
diff --git a/activerecord/test/cases/custom_properties_test.rb b/activerecord/test/cases/custom_properties_test.rb
new file mode 100644
index 0000000000..9598f0299c
--- /dev/null
+++ b/activerecord/test/cases/custom_properties_test.rb
@@ -0,0 +1,64 @@
+require 'cases/helper'
+
+class OverloadedType < ActiveRecord::Base
+ property :overloaded_float, Type::Integer.new
+ property :overloaded_string_with_limit, Type::String.new(limit: 50)
+ property :non_existent_decimal, Type::Decimal.new
+end
+
+class UnoverloadedType < ActiveRecord::Base
+ self.table_name = 'overloaded_types'
+end
+
+module ActiveRecord
+ class CustomPropertiesTest < ActiveRecord::TestCase
+ def test_overloading_types
+ data = OverloadedType.new
+
+ data.overloaded_float = "1.1"
+ data.unoverloaded_float = "1.1"
+
+ assert_equal 1, data.overloaded_float
+ assert_equal 1.1, data.unoverloaded_float
+ end
+
+ def test_overloaded_properties_save
+ data = OverloadedType.new
+
+ data.overloaded_float = "2.2"
+ data.save!
+ data.reload
+
+ assert_equal 2, data.overloaded_float
+ assert_equal 2.0, UnoverloadedType.last.overloaded_float
+ end
+
+ def test_properties_assigned_in_constructor
+ data = OverloadedType.new(overloaded_float: '3.3')
+
+ assert_equal 3, data.overloaded_float
+ end
+
+ def test_overloaded_properties_with_limit
+ assert_equal 50, OverloadedType.columns_hash['overloaded_string_with_limit'].limit
+ assert_equal 255, UnoverloadedType.columns_hash['overloaded_string_with_limit'].limit
+ end
+
+ def test_nonexistent_property
+ data = OverloadedType.new(non_existent_decimal: 1)
+
+ assert_equal BigDecimal.new(1), data.non_existent_decimal
+ assert_raise ActiveRecord::UnknownAttributeError do
+ UnoverloadedType.new(non_existent_decimal: 1)
+ end
+ end
+
+ def test_overloaded_properties_have_no_default
+ data = OverloadedType.new
+ unoverloaded_data = UnoverloadedType.new
+
+ assert_nil data.overloaded_float
+ assert unoverloaded_data.overloaded_float
+ end
+ end
+end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index eaf2cada9d..937646b09a 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -9,6 +9,7 @@ require 'active_record'
require 'cases/test_case'
require 'active_support/dependencies'
require 'active_support/logger'
+require 'active_support/core_ext/string/strip'
require 'support/config'
require 'support/connection'
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 455ec78f68..aa679d4a35 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -11,7 +11,13 @@ require MIGRATIONS_ROOT + "/rename/1_we_need_things"
require MIGRATIONS_ROOT + "/rename/2_rename_things"
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
-class BigNumber < ActiveRecord::Base; end
+class BigNumber < ActiveRecord::Base
+ unless current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter)
+ property :value_of_e, Type::Integer.new
+ end
+ property :world_population, Type::Integer.new
+ property :my_house_population, Type::Integer.new
+end
class Reminder < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 56d0dd6a77..c719918fd7 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -149,38 +149,6 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_equal k.connection.quote_column_name("foo"), k.quoted_primary_key
end
- def test_two_models_with_same_table_but_different_primary_key
- k1 = Class.new(ActiveRecord::Base)
- k1.table_name = 'posts'
- k1.primary_key = 'id'
-
- k2 = Class.new(ActiveRecord::Base)
- k2.table_name = 'posts'
- k2.primary_key = 'title'
-
- assert k1.columns.find { |c| c.name == 'id' }.primary
- assert !k1.columns.find { |c| c.name == 'title' }.primary
- assert k1.columns_hash['id'].primary
- assert !k1.columns_hash['title'].primary
-
- assert !k2.columns.find { |c| c.name == 'id' }.primary
- assert k2.columns.find { |c| c.name == 'title' }.primary
- assert !k2.columns_hash['id'].primary
- assert k2.columns_hash['title'].primary
- end
-
- def test_models_with_same_table_have_different_columns
- k1 = Class.new(ActiveRecord::Base)
- k1.table_name = 'posts'
-
- k2 = Class.new(ActiveRecord::Base)
- k2.table_name = 'posts'
-
- k1.columns.zip(k2.columns).each do |col1, col2|
- assert !col1.equal?(col2)
- end
- end
-
def test_auto_detect_primary_key_from_schema
MixedCaseMonkey.reset_primary_key
assert_equal "monkeyID", MixedCaseMonkey.primary_key
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index e2439b9a24..bbd5298da1 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -3,14 +3,6 @@ require "cases/helper"
module ActiveRecord
module ConnectionAdapters
class QuotingTest < ActiveRecord::TestCase
- class FakeColumn < ActiveRecord::ConnectionAdapters::Column
- attr_accessor :type
-
- def initialize type
- @type = type
- end
- end
-
def setup
@quoter = Class.new { include Quoting }.new
end
@@ -101,12 +93,12 @@ module ActiveRecord
def test_quote_true
assert_equal @quoter.quoted_true, @quoter.quote(true, nil)
- assert_equal '1', @quoter.quote(true, Struct.new(:type).new(:integer))
+ assert_equal '1', @quoter.quote(true, Type::Integer.new)
end
def test_quote_false
assert_equal @quoter.quoted_false, @quoter.quote(false, nil)
- assert_equal '0', @quoter.quote(false, Struct.new(:type).new(:integer))
+ assert_equal '0', @quoter.quote(false, Type::Integer.new)
end
def test_quote_float
@@ -166,26 +158,26 @@ module ActiveRecord
end
def test_quote_string_int_column
- assert_equal "1", @quoter.quote('1', FakeColumn.new(:integer))
- assert_equal "1", @quoter.quote('1.2', FakeColumn.new(:integer))
+ assert_equal "1", @quoter.quote('1', Type::Integer.new)
+ assert_equal "1", @quoter.quote('1.2', Type::Integer.new)
end
def test_quote_string_float_column
- assert_equal "1.0", @quoter.quote('1', FakeColumn.new(:float))
- assert_equal "1.2", @quoter.quote('1.2', FakeColumn.new(:float))
+ assert_equal "1.0", @quoter.quote('1', Type::Float.new)
+ assert_equal "1.2", @quoter.quote('1.2', Type::Float.new)
end
def test_quote_as_mb_chars_binary_column
string = ActiveSupport::Multibyte::Chars.new('lo\l')
- assert_equal "'lo\\\\l'", @quoter.quote(string, FakeColumn.new(:binary))
+ assert_equal "'lo\\\\l'", @quoter.quote(string, Type::Binary.new)
end
def test_quote_binary_without_string_to_binary
- assert_equal "'lo\\\\l'", @quoter.quote('lo\l', FakeColumn.new(:binary))
+ assert_equal "'lo\\\\l'", @quoter.quote('lo\l', Type::Binary.new)
end
def test_string_with_crazy_column
- assert_equal "'lo\\\\l'", @quoter.quote('lo\l', FakeColumn.new(:foo))
+ assert_equal "'lo\\\\l'", @quoter.quote('lo\l')
end
def test_quote_duration
@@ -193,7 +185,7 @@ module ActiveRecord
end
def test_quote_duration_int_column
- assert_equal "7200", @quoter.quote(2.hours, FakeColumn.new(:integer))
+ assert_equal "7200", @quoter.quote(2.hours, Type::Integer.new)
end
end
end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index c085fcf161..e6603f28be 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -200,7 +200,12 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_reflection_should_not_raise_error_when_compared_to_other_object
- assert_nothing_raised { Firm.reflections['clients'] == Object.new }
+ assert_not_equal Object.new, Firm._reflections['clients']
+ end
+
+ def test_has_and_belongs_to_many_reflection
+ assert_equal :has_and_belongs_to_many, Category.reflections['posts'].macro
+ assert_equal :posts, Category.reflect_on_all_associations(:has_and_belongs_to_many).first.name
end
def test_has_many_through_reflection
diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb
index c6decaad89..b9e69bdb08 100644
--- a/activerecord/test/cases/relation/where_chain_test.rb
+++ b/activerecord/test/cases/relation/where_chain_test.rb
@@ -99,7 +99,7 @@ module ActiveRecord
assert_bound_ast value, Post.arel_table[@name], Arel::Nodes::NotEqual
assert_equal 'ruby on rails', bind.last
end
-
+
def test_rewhere_with_one_condition
relation = Post.where(title: 'hello').where(title: 'world').rewhere(title: 'alone')
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 4b146c11bc..88df997a2f 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -552,13 +552,6 @@ class RelationTest < ActiveRecord::TestCase
end
end
- def test_deep_preload
- post = Post.preload(author: :posts, comments: :post).first
-
- assert_predicate post.author.association(:posts), :loaded?
- assert_predicate post.comments.first.association(:post), :loaded?
- end
-
def test_preload_applies_to_all_chained_preloaded_scopes
assert_queries(3) do
post = Post.with_comments.with_tags.first
@@ -750,6 +743,13 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [], relation.to_a
end
+ def test_typecasting_where_with_array
+ ids = Author.pluck(:id)
+ slugs = ids.map { |id| "#{id}-as-a-slug" }
+
+ assert_equal Author.all.to_a, Author.where(id: slugs).to_a
+ end
+
def test_find_all_using_where_with_relation
david = authors(:david)
# switching the lines below would succeed in current rails
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 61bca976f7..9602252b2e 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -1,10 +1,8 @@
require "cases/helper"
class SchemaDumperTest < ActiveRecord::TestCase
- def setup
- super
+ setup do
ActiveRecord::SchemaMigration.create_table
- @stream = StringIO.new
end
def standard_dump
@@ -25,7 +23,8 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
def test_magic_comment
- assert_match "# encoding: #{@stream.external_encoding.name}", standard_dump
+ output = standard_dump
+ assert_match "# encoding: #{@stream.external_encoding.name}", output
end
def test_schema_dump
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index c46060a646..7dd1f10ce9 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -1,8 +1,11 @@
require "cases/helper"
require 'models/contact'
require 'models/topic'
+require 'models/book'
class SerializationTest < ActiveRecord::TestCase
+ fixtures :books
+
FORMATS = [ :xml, :json ]
def setup
@@ -65,4 +68,20 @@ class SerializationTest < ActiveRecord::TestCase
ensure
ActiveRecord::Base.include_root_in_json = original_root_in_json
end
+
+ def test_read_attribute_for_serialization_with_format_after_init
+ klazz = Class.new(ActiveRecord::Base)
+ klazz.table_name = 'books'
+
+ book = klazz.new(format: 'paperback')
+ assert_equal 'paperback', book.read_attribute_for_serialization(:format)
+ end
+
+ def test_read_attribute_for_serialization_with_format_after_find
+ klazz = Class.new(ActiveRecord::Base)
+ klazz.table_name = 'books'
+
+ book = klazz.find(books(:awdr).id)
+ assert_equal 'paperback', book.read_attribute_for_serialization(:format)
+ end
end
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
new file mode 100644
index 0000000000..5d5f442d3a
--- /dev/null
+++ b/activerecord/test/cases/types_test.rb
@@ -0,0 +1,159 @@
+require "cases/helper"
+require 'models/company'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class TypesTest < ActiveRecord::TestCase
+ def test_type_cast_boolean
+ type = Type::Boolean.new
+ assert type.type_cast('').nil?
+ assert type.type_cast(nil).nil?
+
+ assert type.type_cast(true)
+ assert type.type_cast(1)
+ assert type.type_cast('1')
+ assert type.type_cast('t')
+ assert type.type_cast('T')
+ assert type.type_cast('true')
+ assert type.type_cast('TRUE')
+ assert type.type_cast('on')
+ assert type.type_cast('ON')
+
+ # explicitly check for false vs nil
+ assert_equal false, type.type_cast(false)
+ assert_equal false, type.type_cast(0)
+ assert_equal false, type.type_cast('0')
+ assert_equal false, type.type_cast('f')
+ assert_equal false, type.type_cast('F')
+ assert_equal false, type.type_cast('false')
+ assert_equal false, type.type_cast('FALSE')
+ assert_equal false, type.type_cast('off')
+ assert_equal false, type.type_cast('OFF')
+ assert_equal false, type.type_cast(' ')
+ assert_equal false, type.type_cast("\u3000\r\n")
+ assert_equal false, type.type_cast("\u0000")
+ assert_equal false, type.type_cast('SOMETHING RANDOM')
+ end
+
+ def test_type_cast_string
+ type = Type::String.new
+ assert_equal "1", type.type_cast(true)
+ assert_equal "0", type.type_cast(false)
+ assert_equal "123", type.type_cast(123)
+ end
+
+ def test_type_cast_integer
+ type = Type::Integer.new
+ assert_equal 1, type.type_cast(1)
+ assert_equal 1, type.type_cast('1')
+ assert_equal 1, type.type_cast('1ignore')
+ assert_equal 0, type.type_cast('bad1')
+ assert_equal 0, type.type_cast('bad')
+ assert_equal 1, type.type_cast(1.7)
+ assert_equal 0, type.type_cast(false)
+ assert_equal 1, type.type_cast(true)
+ assert_nil type.type_cast(nil)
+ end
+
+ def test_type_cast_non_integer_to_integer
+ type = Type::Integer.new
+ assert_nil type.type_cast([1,2])
+ assert_nil type.type_cast({1 => 2})
+ assert_nil type.type_cast((1..2))
+ end
+
+ def test_type_cast_activerecord_to_integer
+ type = Type::Integer.new
+ firm = Firm.create(:name => 'Apple')
+ assert_nil type.type_cast(firm)
+ end
+
+ def test_type_cast_object_without_to_i_to_integer
+ type = Type::Integer.new
+ assert_nil type.type_cast(Object.new)
+ end
+
+ def test_type_cast_nan_and_infinity_to_integer
+ type = Type::Integer.new
+ assert_nil type.type_cast(Float::NAN)
+ assert_nil type.type_cast(1.0/0.0)
+ end
+
+ def test_type_cast_float
+ type = Type::Float.new
+ assert_equal 1.0, type.type_cast("1")
+ end
+
+ def test_type_cast_decimal
+ type = Type::Decimal.new
+ assert_equal BigDecimal.new("0"), type.type_cast(BigDecimal.new("0"))
+ assert_equal BigDecimal.new("123"), type.type_cast(123.0)
+ assert_equal BigDecimal.new("1"), type.type_cast(:"1")
+ end
+
+ def test_type_cast_binary
+ type = Type::Binary.new
+ assert_equal nil, type.type_cast(nil)
+ assert_equal "1", type.type_cast("1")
+ assert_equal 1, type.type_cast(1)
+ end
+
+ def test_type_cast_time
+ type = Type::Time.new
+ assert_equal nil, type.type_cast(nil)
+ assert_equal nil, type.type_cast('')
+ assert_equal nil, type.type_cast('ABC')
+
+ time_string = Time.now.utc.strftime("%T")
+ assert_equal time_string, type.type_cast(time_string).strftime("%T")
+ end
+
+ def test_type_cast_datetime_and_timestamp
+ type = Type::DateTime.new
+ assert_equal nil, type.type_cast(nil)
+ assert_equal nil, type.type_cast('')
+ assert_equal nil, type.type_cast(' ')
+ assert_equal nil, type.type_cast('ABC')
+
+ datetime_string = Time.now.utc.strftime("%FT%T")
+ assert_equal datetime_string, type.type_cast(datetime_string).strftime("%FT%T")
+ end
+
+ def test_type_cast_date
+ type = Type::Date.new
+ assert_equal nil, type.type_cast(nil)
+ assert_equal nil, type.type_cast('')
+ assert_equal nil, type.type_cast(' ')
+ assert_equal nil, type.type_cast('ABC')
+
+ date_string = Time.now.utc.strftime("%F")
+ assert_equal date_string, type.type_cast(date_string).strftime("%F")
+ end
+
+ def test_type_cast_duration_to_integer
+ type = Type::Integer.new
+ assert_equal 1800, type.type_cast(30.minutes)
+ assert_equal 7200, type.type_cast(2.hours)
+ end
+
+ def test_string_to_time_with_timezone
+ [:utc, :local].each do |zone|
+ with_timezone_config default: zone do
+ type = Type::DateTime.new
+ assert_equal Time.utc(2013, 9, 4, 0, 0, 0), type.type_cast("Wed, 04 Sep 2013 03:00:00 EAT")
+ end
+ end
+ end
+
+ if current_adapter?(:SQLite3Adapter)
+ def test_binary_encoding
+ type = SQLite3Binary.new
+ utf8_string = "a string".encode(Encoding::UTF_8)
+ type_cast = type.type_cast(utf8_string)
+
+ assert_equal Encoding::ASCII_8BIT, type_cast.encoding
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/fixtures/books.yml b/activerecord/test/fixtures/books.yml
index fb48645456..abe56752c6 100644
--- a/activerecord/test/fixtures/books.yml
+++ b/activerecord/test/fixtures/books.yml
@@ -2,8 +2,10 @@ awdr:
author_id: 1
id: 1
name: "Agile Web Development with Rails"
+ format: "paperback"
rfr:
author_id: 1
id: 2
name: "Ruby for Rails"
+ format: "ebook"
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 8c52ad2724..4cce58f4f4 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -103,6 +103,7 @@ ActiveRecord::Schema.define do
create_table :books, force: true do |t|
t.integer :author_id
+ t.string :format
t.column :name, :string
t.column :status, :integer, default: 0
t.column :read_status, :integer, default: 0
@@ -854,6 +855,12 @@ ActiveRecord::Schema.define do
execute "ALTER TABLE lessons_students ADD CONSTRAINT student_id_fk FOREIGN KEY (#{quote_column_name 'student_id'}) REFERENCES #{quote_table_name 'students'} (#{quote_column_name 'id'})"
end
+
+ create_table :overloaded_types, force: true do |t|
+ t.float :overloaded_float, default: 500
+ t.float :unoverloaded_float
+ t.string :overloaded_string_with_limit, limit: 255
+ end
end
Course.connection.create_table :courses, force: true do |t|
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 2a992f60bb..6915a491b0 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,17 @@
+* DateTime `advance` now supports partial days.
+
+ Before:
+
+ DateTime.now.advance(days: 1, hours: 12)
+
+ After:
+
+ DateTime.now.advance(days: 1.5)
+
+ Fixes #12005.
+
+ *Shay Davidson*
+
* `Hash#deep_transform_keys` and `Hash#deep_transform_keys!` now transform hashes
in nested arrays. This change also applies to `Hash#deep_stringify_keys`,
`Hash#deep_stringify_keys!`, `Hash#deep_symbolize_keys` and
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index 67f58bc0fe..caa499dfa2 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -5,6 +5,8 @@ class Array
# %w( a b c d ).from(2) # => ["c", "d"]
# %w( a b c d ).from(10) # => []
# %w().from(0) # => []
+ # %w( a b c d ).from(-2) # => ["c", "d"]
+ # %w( a b c ).from(-10) # => []
def from(position)
self[position, length] || []
end
@@ -15,8 +17,10 @@ class Array
# %w( a b c d ).to(2) # => ["a", "b", "c"]
# %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
# %w().to(0) # => []
+ # %w( a b c d ).to(-2) # => ["a", "b", "c"]
+ # %w( a b c ).to(-10) # => []
def to(position)
- first position + 1
+ self[0..position]
end
# Equal to <tt>self[1]</tt>.
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index 73ad0aa097..289ca12b5e 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -53,6 +53,16 @@ class DateTime
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
# <tt>:minutes</tt>, <tt>:seconds</tt>.
def advance(options)
+ unless options[:weeks].nil?
+ options[:weeks], partial_weeks = options[:weeks].divmod(1)
+ options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
+ end
+
+ unless options[:days].nil?
+ options[:days], partial_days = options[:days].divmod(1)
+ options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
+ end
+
d = to_date.advance(options)
datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
seconds_to_advance = \
@@ -63,7 +73,7 @@ class DateTime
if seconds_to_advance.zero?
datetime_advanced_by_date
else
- datetime_advanced_by_date.since seconds_to_advance
+ datetime_advanced_by_date.since(seconds_to_advance)
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 59675d744e..8a545e4386 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -180,10 +180,11 @@ module ActiveSupport #:nodoc:
Dependencies.load_missing_constant(from_mod, const_name)
end
- # Dependencies assumes the name of the module reflects the nesting (unless
- # it can be proven that is not the case), and the path to the file that
- # defines the constant. Anonymous modules cannot follow these conventions
- # and we assume therefore the user wants to refer to a top-level constant.
+ # We assume that the name of the module reflects the nesting
+ # (unless it can be proven that is not the case) and the path to the file
+ # that defines the constant. Anonymous modules cannot follow these
+ # conventions and therefore we assume that the user wants to refer to a
+ # top-level constant.
def guess_for_anonymous(const_name)
if Object.const_defined?(const_name)
raise NameError, "#{const_name} cannot be autoloaded from an anonymous class or module"
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 09eb732ef7..0ae641d05b 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -105,8 +105,7 @@ module ActiveSupport
# We define it as a workaround to Ruby 2.0.0-p353 bug.
# For more information, check rails/rails#13055.
- # It should be dropped once a new Ruby patch-level
- # release after 2.0.0-p353 happens.
+ # Remove it when we drop support for 2.0.0-p353.
def ===(other) #:nodoc:
value === other
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 2fb5c04316..e6c125bfdd 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -1,5 +1,3 @@
-gem 'minitest' # make sure we get the gem, not stdlib
-require 'minitest'
require 'active_support/testing/tagged_logging'
require 'active_support/testing/setup_and_teardown'
require 'active_support/testing/assertions'
diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb
index 92a593965e..ea2d3391bd 100644
--- a/activesupport/lib/active_support/time.rb
+++ b/activesupport/lib/active_support/time.rb
@@ -1,5 +1,3 @@
-require 'active_support'
-
module ActiveSupport
autoload :Duration, 'active_support/duration'
autoload :TimeWithZone, 'active_support/time_with_zone'
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index e0e54f47e4..bd1b818717 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -10,12 +10,16 @@ class ArrayExtAccessTests < ActiveSupport::TestCase
assert_equal %w( a b c d ), %w( a b c d ).from(0)
assert_equal %w( c d ), %w( a b c d ).from(2)
assert_equal %w(), %w( a b c d ).from(10)
+ assert_equal %w( d e ), %w( a b c d e ).from(-2)
+ assert_equal %w(), %w( a b c d e ).from(-10)
end
def test_to
assert_equal %w( a ), %w( a b c d ).to(0)
assert_equal %w( a b c ), %w( a b c d ).to(2)
assert_equal %w( a b c d ), %w( a b c d ).to(10)
+ assert_equal %w( a b c ), %w( a b c d ).to(-2)
+ assert_equal %w(), %w( a b c ).to(-10)
end
def test_second_through_tenth
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 0a40aeb96c..224172e39f 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -162,6 +162,12 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal DateTime.civil(2013,10,17,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9)
end
+ def test_advance_partial_days
+ assert_equal DateTime.civil(2012,9,29,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(:days => 1.5)
+ assert_equal DateTime.civil(2012,9,28,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(:days => 0.5)
+ assert_equal DateTime.civil(2012,10,29,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(:days => 1.5, :months => 1)
+ end
+
def test_advanced_processes_first_the_date_deltas_and_then_the_time_deltas
# If the time deltas were processed first, the following datetimes would be advanced to 2010/04/01 instead.
assert_equal DateTime.civil(2010, 3, 29), DateTime.civil(2010, 2, 28, 23, 59, 59).advance(:months => 1, :seconds => 1)
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index b0b4738eb3..eb8b0d878e 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -498,10 +498,10 @@ class InflectorTest < ActiveSupport::TestCase
end
%w(plurals singulars uncountables humans acronyms).each do |scope|
- ActiveSupport::Inflector.inflections do |inflect|
- define_method("test_clear_inflections_with_#{scope}") do
- with_dup do
- # clear the inflections
+ define_method("test_clear_inflections_with_#{scope}") do
+ with_dup do
+ # clear the inflections
+ ActiveSupport::Inflector.inflections do |inflect|
inflect.clear(scope)
assert_equal [], inflect.send(scope)
end
@@ -516,9 +516,10 @@ class InflectorTest < ActiveSupport::TestCase
# there are module functions that access ActiveSupport::Inflector.inflections,
# so we need to replace the singleton itself.
def with_dup
- original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)
- ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, original.dup)
+ original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en]
+ ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup)
+ yield
ensure
- ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, original)
+ ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original)
end
end
diff --git a/activesupport/test/subscriber_test.rb b/activesupport/test/subscriber_test.rb
index 8be8c51f07..21e4ba0cee 100644
--- a/activesupport/test/subscriber_test.rb
+++ b/activesupport/test/subscriber_test.rb
@@ -23,6 +23,7 @@ end
# Monkey patch subscriber to test that only one subscriber per method is added.
class TestSubscriber
+ remove_method :open_party
def open_party(event)
events << event
end
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index 0b52db5670..4cd4a9d70c 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1,3 +1,17 @@
+* Change Posts to Articles in Getting Started sample application in order to
+better align with the actual guides.
+
+ * John Kelly Ferguson*
+
+* Update all Rails 4.1.0 references to 4.1.1 within the guides and code.
+
+ * John Kelly Ferguson*
+
+* Split up rows in the Explain Queries table of the ActiveRecord Querying section
+in order to improve readability.
+
+ * John Kelly Ferguson*
+
* Change all non-HTTP method 'post' references to 'article'.
*John Kelly Ferguson*
diff --git a/guides/code/getting_started/Gemfile b/guides/code/getting_started/Gemfile
index 091a87aa4c..13a0ef44a9 100644
--- a/guides/code/getting_started/Gemfile
+++ b/guides/code/getting_started/Gemfile
@@ -2,11 +2,11 @@ source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
-gem 'rails', '4.1.0'
+gem 'rails', '4.1.1'
# Use SQLite3 as the database for Active Record
gem 'sqlite3'
# Use SCSS for stylesheets
-gem 'sass-rails', '~> 4.0.1'
+gem 'sass-rails', '~> 4.0.3'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
diff --git a/guides/code/getting_started/Gemfile.lock b/guides/code/getting_started/Gemfile.lock
index a2ab76c908..f26cf58e6a 100644
--- a/guides/code/getting_started/Gemfile.lock
+++ b/guides/code/getting_started/Gemfile.lock
@@ -1,34 +1,33 @@
GEM
remote: https://rubygems.org/
specs:
- actionmailer (4.1.0)
- actionpack (= 4.1.0)
- actionview (= 4.1.0)
+ actionmailer (4.1.1)
+ actionpack (= 4.1.1)
+ actionview (= 4.1.1)
mail (~> 2.5.4)
- actionpack (4.1.0)
- actionview (= 4.1.0)
- activesupport (= 4.1.0)
+ actionpack (4.1.1)
+ actionview (= 4.1.1)
+ activesupport (= 4.1.1)
rack (~> 1.5.2)
rack-test (~> 0.6.2)
- actionview (4.1.0)
- activesupport (= 4.1.0)
+ actionview (4.1.1)
+ activesupport (= 4.1.1)
builder (~> 3.1)
erubis (~> 2.7.0)
- activemodel (4.1.0)
- activesupport (= 4.1.0)
+ activemodel (4.1.1)
+ activesupport (= 4.1.1)
builder (~> 3.1)
- activerecord (4.1.0)
- activemodel (= 4.1.0)
- activesupport (= 4.1.0)
+ activerecord (4.1.1)
+ activemodel (= 4.1.1)
+ activesupport (= 4.1.1)
arel (~> 5.0.0)
- activesupport (4.1.0)
+ activesupport (4.1.1)
i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.1)
tzinfo (~> 1.1)
- arel (5.0.0)
- atomic (1.1.14)
+ arel (5.0.1.20140414130214)
builder (3.2.2)
coffee-rails (4.0.1)
coffee-script (>= 2.2.0)
@@ -52,52 +51,52 @@ GEM
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.25.1)
- minitest (5.2.1)
- multi_json (1.8.4)
- polyglot (0.3.3)
+ minitest (5.3.4)
+ multi_json (1.10.1)
+ polyglot (0.3.4)
rack (1.5.2)
rack-test (0.6.2)
rack (>= 1.0)
- rails (4.1.0)
- actionmailer (= 4.1.0)
- actionpack (= 4.1.0)
- actionview (= 4.1.0)
- activemodel (= 4.1.0)
- activerecord (= 4.1.0)
- activesupport (= 4.1.0)
+ rails (4.1.1)
+ actionmailer (= 4.1.1)
+ actionpack (= 4.1.1)
+ actionview (= 4.1.1)
+ activemodel (= 4.1.1)
+ activerecord (= 4.1.1)
+ activesupport (= 4.1.1)
bundler (>= 1.3.0, < 2.0)
- railties (= 4.1.0)
- sprockets-rails (~> 2.0.0)
- railties (4.1.0)
- actionpack (= 4.1.0)
- activesupport (= 4.1.0)
+ railties (= 4.1.1)
+ sprockets-rails (~> 2.0)
+ railties (4.1.1)
+ actionpack (= 4.1.1)
+ activesupport (= 4.1.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
- rake (10.1.1)
+ rake (10.3.2)
rdoc (4.1.1)
json (~> 1.4)
- sass (3.2.13)
- sass-rails (4.0.1)
+ sass (3.2.19)
+ sass-rails (4.0.3)
railties (>= 4.0.0, < 5.0)
- sass (>= 3.1.10)
- sprockets-rails (~> 2.0.0)
+ sass (~> 3.2.0)
+ sprockets (~> 2.8, <= 2.11.0)
+ sprockets-rails (~> 2.0)
sdoc (0.4.0)
json (~> 1.8)
rdoc (~> 4.0, < 5.0)
spring (1.0.0)
- sprockets (2.10.1)
+ sprockets (2.11.0)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
- sprockets-rails (2.0.1)
+ sprockets-rails (2.1.3)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (~> 2.8)
sqlite3 (1.3.8)
- thor (0.18.1)
- thread_safe (0.1.3)
- atomic
+ thor (0.19.1)
+ thread_safe (0.3.3)
tilt (1.4.1)
treetop (1.4.15)
polyglot
@@ -117,8 +116,8 @@ DEPENDENCIES
coffee-rails (~> 4.0.0)
jbuilder (~> 2.0)
jquery-rails
- rails (= 4.1.0)
- sass-rails (~> 4.0.1)
+ rails (= 4.1.1)
+ sass-rails (~> 4.0.3)
sdoc (~> 0.4.0)
spring
sqlite3
diff --git a/guides/code/getting_started/app/assets/javascripts/posts.js.coffee b/guides/code/getting_started/app/assets/javascripts/articles.js.coffee
index 24f83d18bb..24f83d18bb 100644
--- a/guides/code/getting_started/app/assets/javascripts/posts.js.coffee
+++ b/guides/code/getting_started/app/assets/javascripts/articles.js.coffee
diff --git a/guides/code/getting_started/app/assets/stylesheets/posts.css.scss b/guides/code/getting_started/app/assets/stylesheets/articles.css.scss
index 1a7e15390c..cca548710d 100644
--- a/guides/code/getting_started/app/assets/stylesheets/posts.css.scss
+++ b/guides/code/getting_started/app/assets/stylesheets/articles.css.scss
@@ -1,3 +1,3 @@
-// Place all the styles related to the posts controller here.
+// Place all the styles related to the articles controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/guides/code/getting_started/app/controllers/articles_controller.rb b/guides/code/getting_started/app/controllers/articles_controller.rb
new file mode 100644
index 0000000000..275b84e8b7
--- /dev/null
+++ b/guides/code/getting_started/app/controllers/articles_controller.rb
@@ -0,0 +1,53 @@
+class ArticlesController < ApplicationController
+
+ http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
+
+ def index
+ @articles = Article.all
+ end
+
+ def show
+ @article = Article.find(params[:id])
+ end
+
+ def edit
+ @article = Article.find(params[:id])
+ end
+
+ def update
+ @article = Article.find(params[:id])
+
+ if @article.update(article_params)
+ redirect_to action: :show, id: @article.id
+ else
+ render 'edit'
+ end
+ end
+
+ def new
+ @article = Article.new
+ end
+
+ def create
+ @article = Article.new(article_params)
+
+ if @article.save
+ redirect_to action: :show, id: @article.id
+ else
+ render 'new'
+ end
+ end
+
+ def destroy
+ @article = Article.find(params[:id])
+ @article.destroy
+
+ redirect_to action: :index
+ end
+
+ private
+
+ def article_params
+ params.require(:article).permit(:title, :text)
+ end
+end
diff --git a/guides/code/getting_started/app/controllers/comments_controller.rb b/guides/code/getting_started/app/controllers/comments_controller.rb
index b2d9bcdf7f..61813b1003 100644
--- a/guides/code/getting_started/app/controllers/comments_controller.rb
+++ b/guides/code/getting_started/app/controllers/comments_controller.rb
@@ -3,16 +3,16 @@ class CommentsController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy
def create
- @post = Post.find(params[:post_id])
- @comment = @post.comments.create(comment_params)
- redirect_to post_path(@post)
+ @article = Article.find(params[:article_id])
+ @comment = @article.comments.create(comment_params)
+ redirect_to article_path(@article)
end
def destroy
- @post = Post.find(params[:post_id])
- @comment = @post.comments.find(params[:id])
+ @article = Article.find(params[:article_id])
+ @comment = @article.comments.find(params[:id])
@comment.destroy
- redirect_to post_path(@post)
+ redirect_to article_path(@article)
end
private
diff --git a/guides/code/getting_started/app/controllers/posts_controller.rb b/guides/code/getting_started/app/controllers/posts_controller.rb
deleted file mode 100644
index 02689ad67b..0000000000
--- a/guides/code/getting_started/app/controllers/posts_controller.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-class PostsController < ApplicationController
-
- http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
-
- def index
- @posts = Post.all
- end
-
- def show
- @post = Post.find(params[:id])
- end
-
- def edit
- @post = Post.find(params[:id])
- end
-
- def update
- @post = Post.find(params[:id])
-
- if @post.update(post_params)
- redirect_to action: :show, id: @post.id
- else
- render 'edit'
- end
- end
-
- def new
- @post = Post.new
- end
-
- def create
- @post = Post.new(post_params)
-
- if @post.save
- redirect_to action: :show, id: @post.id
- else
- render 'new'
- end
- end
-
- def destroy
- @post = Post.find(params[:id])
- @post.destroy
-
- redirect_to action: :index
- end
-
- private
-
- def post_params
- params.require(:post).permit(:title, :text)
- end
-end
diff --git a/guides/code/getting_started/app/helpers/articles_helper.rb b/guides/code/getting_started/app/helpers/articles_helper.rb
new file mode 100644
index 0000000000..2968277595
--- /dev/null
+++ b/guides/code/getting_started/app/helpers/articles_helper.rb
@@ -0,0 +1,2 @@
+module ArticlesHelper
+end
diff --git a/guides/code/getting_started/app/helpers/posts_helper.rb b/guides/code/getting_started/app/helpers/posts_helper.rb
deleted file mode 100644
index a7b8cec898..0000000000
--- a/guides/code/getting_started/app/helpers/posts_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module PostsHelper
-end
diff --git a/guides/code/getting_started/app/models/post.rb b/guides/code/getting_started/app/models/article.rb
index 64e0d721fd..6fc7888be2 100644
--- a/guides/code/getting_started/app/models/post.rb
+++ b/guides/code/getting_started/app/models/article.rb
@@ -1,6 +1,6 @@
-class Post < ActiveRecord::Base
+class Article < ActiveRecord::Base
has_many :comments, dependent: :destroy
-
+
validates :title,
presence: true,
length: { minimum: 5 }
diff --git a/guides/code/getting_started/app/models/comment.rb b/guides/code/getting_started/app/models/comment.rb
index 4e76c5b5b0..e2646a324f 100644
--- a/guides/code/getting_started/app/models/comment.rb
+++ b/guides/code/getting_started/app/models/comment.rb
@@ -1,3 +1,3 @@
class Comment < ActiveRecord::Base
- belongs_to :post
+ belongs_to :article
end
diff --git a/guides/code/getting_started/app/views/posts/_form.html.erb b/guides/code/getting_started/app/views/articles/_form.html.erb
index f2f83585e1..87e3353ed2 100644
--- a/guides/code/getting_started/app/views/posts/_form.html.erb
+++ b/guides/code/getting_started/app/views/articles/_form.html.erb
@@ -1,10 +1,10 @@
-<%= form_for @post do |f| %>
- <% if @post.errors.any? %>
+<%= form_for @article do |f| %>
+ <% if @article.errors.any? %>
<div id="error_explanation">
- <h2><%= pluralize(@post.errors.count, "error") %> prohibited
- this post from being saved:</h2>
+ <h2><%= pluralize(@article.errors.count, "error") %> prohibited
+ this article from being saved:</h2>
<ul>
- <% @post.errors.full_messages.each do |msg| %>
+ <% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
diff --git a/guides/code/getting_started/app/views/posts/edit.html.erb b/guides/code/getting_started/app/views/articles/edit.html.erb
index 393e7430d0..14236e2a98 100644
--- a/guides/code/getting_started/app/views/posts/edit.html.erb
+++ b/guides/code/getting_started/app/views/articles/edit.html.erb
@@ -1,5 +1,5 @@
-<h1>Edit post</h1>
-
+<h1>Edit article</h1>
+
<%= render 'form' %>
-
+
<%= link_to 'Back', action: :index %>
diff --git a/guides/code/getting_started/app/views/articles/index.html.erb b/guides/code/getting_started/app/views/articles/index.html.erb
new file mode 100644
index 0000000000..80e9c8c60c
--- /dev/null
+++ b/guides/code/getting_started/app/views/articles/index.html.erb
@@ -0,0 +1,21 @@
+<h1>Listing Articles</h1>
+<table>
+ <tr>
+ <th>Title</th>
+ <th>Text</th>
+ <th></th>
+ <th></th>
+ <th></th>
+ </tr>
+
+<% @articles.each do |article| %>
+ <tr>
+ <td><%= article.title %></td>
+ <td><%= article.text %></td>
+ <td><%= link_to 'Show', action: :show, id: article.id %></td>
+ <td><%= link_to 'Edit', action: :edit, id: article.id %></td>
+ <td><%= link_to 'Destroy', { action: :destroy, id: article.id },
+ method: :delete, data: { confirm: 'Are you sure?' } %></td>
+ </tr>
+<% end %>
+</table>
diff --git a/guides/code/getting_started/app/views/posts/new.html.erb b/guides/code/getting_started/app/views/articles/new.html.erb
index efa81038ec..652b1c9c0b 100644
--- a/guides/code/getting_started/app/views/posts/new.html.erb
+++ b/guides/code/getting_started/app/views/articles/new.html.erb
@@ -1,5 +1,5 @@
-<h1>New post</h1>
-
+<h1>New article</h1>
+
<%= render 'form' %>
-
+
<%= link_to 'Back', action: :index %>
diff --git a/guides/code/getting_started/app/views/articles/show.html.erb b/guides/code/getting_started/app/views/articles/show.html.erb
new file mode 100644
index 0000000000..6959c80bdb
--- /dev/null
+++ b/guides/code/getting_started/app/views/articles/show.html.erb
@@ -0,0 +1,18 @@
+<p>
+ <strong>Title:</strong>
+ <%= @article.title %>
+</p>
+
+<p>
+ <strong>Text:</strong>
+ <%= @article.text %>
+</p>
+
+<h2>Comments</h2>
+<%= render @article.comments %>
+
+<h2>Add a comment:</h2>
+<%= render "comments/form" %>
+
+<%= link_to 'Edit Article', edit_article_path(@article) %> |
+<%= link_to 'Back to Articles', articles_path %>
diff --git a/guides/code/getting_started/app/views/comments/_comment.html.erb b/guides/code/getting_started/app/views/comments/_comment.html.erb
index 593493339e..f7cbfaebfa 100644
--- a/guides/code/getting_started/app/views/comments/_comment.html.erb
+++ b/guides/code/getting_started/app/views/comments/_comment.html.erb
@@ -2,14 +2,14 @@
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
-
+
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
- <%= link_to 'Destroy Comment', [comment.post, comment],
+ <%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
diff --git a/guides/code/getting_started/app/views/comments/_form.html.erb b/guides/code/getting_started/app/views/comments/_form.html.erb
index 00cb3a08f0..5850c41a17 100644
--- a/guides/code/getting_started/app/views/comments/_form.html.erb
+++ b/guides/code/getting_started/app/views/comments/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_for([@post, @post.comments.build]) do |f| %>
+<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
diff --git a/guides/code/getting_started/app/views/posts/index.html.erb b/guides/code/getting_started/app/views/posts/index.html.erb
deleted file mode 100644
index 7369f0396f..0000000000
--- a/guides/code/getting_started/app/views/posts/index.html.erb
+++ /dev/null
@@ -1,21 +0,0 @@
-<h1>Listing Posts</h1>
-<table>
- <tr>
- <th>Title</th>
- <th>Text</th>
- <th></th>
- <th></th>
- <th></th>
- </tr>
-
-<% @posts.each do |post| %>
- <tr>
- <td><%= post.title %></td>
- <td><%= post.text %></td>
- <td><%= link_to 'Show', action: :show, id: post.id %></td>
- <td><%= link_to 'Edit', action: :edit, id: post.id %></td>
- <td><%= link_to 'Destroy', { action: :destroy, id: post.id },
- method: :delete, data: { confirm: 'Are you sure?' } %></td>
- </tr>
-<% end %>
-</table>
diff --git a/guides/code/getting_started/app/views/posts/show.html.erb b/guides/code/getting_started/app/views/posts/show.html.erb
deleted file mode 100644
index e99e9edbb3..0000000000
--- a/guides/code/getting_started/app/views/posts/show.html.erb
+++ /dev/null
@@ -1,18 +0,0 @@
-<p>
- <strong>Title:</strong>
- <%= @post.title %>
-</p>
-
-<p>
- <strong>Text:</strong>
- <%= @post.text %>
-</p>
-
-<h2>Comments</h2>
-<%= render @post.comments %>
-
-<h2>Add a comment:</h2>
-<%= render "comments/form" %>
-
-<%= link_to 'Edit Post', edit_post_path(@post) %> |
-<%= link_to 'Back to Posts', posts_path %>
diff --git a/guides/code/getting_started/app/views/welcome/index.html.erb b/guides/code/getting_started/app/views/welcome/index.html.erb
index 56be8dd3cc..1cabd0d217 100644
--- a/guides/code/getting_started/app/views/welcome/index.html.erb
+++ b/guides/code/getting_started/app/views/welcome/index.html.erb
@@ -1,4 +1,4 @@
<h1>Hello, Rails!</h1>
-<%= link_to "My Blog", controller: "posts" %>
-<%= link_to "New Post", new_post_path %>
+<%= link_to "My Blog", controller: "articles" %>
+<%= link_to "New Article", new_article_path %>
diff --git a/guides/code/getting_started/config/routes.rb b/guides/code/getting_started/config/routes.rb
index 65d273b58d..97abca99b9 100644
--- a/guides/code/getting_started/config/routes.rb
+++ b/guides/code/getting_started/config/routes.rb
@@ -1,5 +1,5 @@
Rails.application.routes.draw do
- resources :posts do
+ resources :articles do
resources :comments
end
diff --git a/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb b/guides/code/getting_started/db/migrate/20130122042648_create_articles.rb
index 602bef31ab..6bb255e89f 100644
--- a/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb
+++ b/guides/code/getting_started/db/migrate/20130122042648_create_articles.rb
@@ -1,6 +1,6 @@
-class CreatePosts < ActiveRecord::Migration
+class CreateArticles < ActiveRecord::Migration
def change
- create_table :posts do |t|
+ create_table :articles do |t|
t.string :title
t.text :text
diff --git a/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb b/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb
index 3e51f9c0f7..1f765839ac 100644
--- a/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb
+++ b/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb
@@ -3,7 +3,7 @@ class CreateComments < ActiveRecord::Migration
create_table :comments do |t|
t.string :commenter
t.text :body
- t.references :post, index: true
+ t.references :article, index: true
t.timestamps
end
diff --git a/guides/code/getting_started/db/schema.rb b/guides/code/getting_started/db/schema.rb
index 101fe712a1..be40f7cb0e 100644
--- a/guides/code/getting_started/db/schema.rb
+++ b/guides/code/getting_started/db/schema.rb
@@ -13,21 +13,21 @@
ActiveRecord::Schema.define(version: 20130122045842) do
- create_table "comments", force: true do |t|
- t.string "commenter"
- t.text "body"
- t.integer "post_id"
+ create_table "articles", force: true do |t|
+ t.string "title"
+ t.text "text"
t.datetime "created_at"
t.datetime "updated_at"
end
- add_index "comments", ["post_id"], name: "index_comments_on_post_id"
-
- create_table "posts", force: true do |t|
- t.string "title"
- t.text "text"
+ create_table "comments", force: true do |t|
+ t.string "commenter"
+ t.text "body"
+ t.integer "article_id"
t.datetime "created_at"
t.datetime "updated_at"
end
+ add_index "comments", ["article_id"], name: "index_comments_on_article_id"
+
end
diff --git a/guides/code/getting_started/test/controllers/posts_controller_test.rb b/guides/code/getting_started/test/controllers/articles_controller_test.rb
index 7a6ee4f1db..361aa0f47f 100644
--- a/guides/code/getting_started/test/controllers/posts_controller_test.rb
+++ b/guides/code/getting_started/test/controllers/articles_controller_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class PostsControllerTest < ActionController::TestCase
+class ArticlesControllerTest < ActionController::TestCase
# test "the truth" do
# assert true
# end
diff --git a/guides/code/getting_started/test/fixtures/posts.yml b/guides/code/getting_started/test/fixtures/articles.yml
index 46b01c3bb4..46b01c3bb4 100644
--- a/guides/code/getting_started/test/fixtures/posts.yml
+++ b/guides/code/getting_started/test/fixtures/articles.yml
diff --git a/guides/code/getting_started/test/fixtures/comments.yml b/guides/code/getting_started/test/fixtures/comments.yml
index 9e409d8a61..05ad26f051 100644
--- a/guides/code/getting_started/test/fixtures/comments.yml
+++ b/guides/code/getting_started/test/fixtures/comments.yml
@@ -3,9 +3,9 @@
one:
commenter: MyString
body: MyText
- post_id:
+ article_id:
two:
commenter: MyString
body: MyText
- post_id:
+ article_id:
diff --git a/guides/code/getting_started/test/helpers/articles_helper_test.rb b/guides/code/getting_started/test/helpers/articles_helper_test.rb
new file mode 100644
index 0000000000..b341344067
--- /dev/null
+++ b/guides/code/getting_started/test/helpers/articles_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class ArticlesHelperTest < ActionView::TestCase
+end
diff --git a/guides/code/getting_started/test/helpers/posts_helper_test.rb b/guides/code/getting_started/test/helpers/posts_helper_test.rb
deleted file mode 100644
index 48549c2ea1..0000000000
--- a/guides/code/getting_started/test/helpers/posts_helper_test.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class PostsHelperTest < ActionView::TestCase
-end
diff --git a/guides/code/getting_started/test/models/post_test.rb b/guides/code/getting_started/test/models/article_test.rb
index 6d9d463a71..11c8abe5f4 100644
--- a/guides/code/getting_started/test/models/post_test.rb
+++ b/guides/code/getting_started/test/models/article_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class PostTest < ActiveSupport::TestCase
+class ArticleTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
diff --git a/guides/code/getting_started/test/test_helper.rb b/guides/code/getting_started/test/test_helper.rb
index f91a4375dc..ecbaf77ea7 100644
--- a/guides/code/getting_started/test/test_helper.rb
+++ b/guides/code/getting_started/test/test_helper.rb
@@ -6,9 +6,6 @@ class ActiveSupport::TestCase
ActiveRecord::Migration.check_pending!
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
- #
- # Note: You'll currently still have to declare fixtures explicitly in integration tests
- # -- they do not yet inherit this setting
fixtures :all
# Add more helper methods to be used by all tests here...
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 697ddd70cb..ee8cf4ade6 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1703,12 +1703,19 @@ may yield
```
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `articles` ON `articles`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
-+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
-| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
-| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
-| 1 | SIMPLE | articles | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
-+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
++----+-------------+----------+-------+---------------+
+| id | select_type | table | type | possible_keys |
++----+-------------+----------+-------+---------------+
+| 1 | SIMPLE | users | const | PRIMARY |
+| 1 | SIMPLE | articles | ALL | NULL |
++----+-------------+----------+-------+---------------+
++---------+---------+-------+------+-------------+
+| key | key_len | ref | rows | Extra |
++---------+---------+-------+------+-------------+
+| PRIMARY | 4 | const | 1 | |
+| NULL | NULL | NULL | 1 | Using where |
++---------+---------+-------+------+-------------+
+
2 rows in set (0.00 sec)
```
@@ -1742,19 +1749,32 @@ yields
```
EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
-+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
-| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
-| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
-+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
++----+-------------+-------+-------+---------------+
+| id | select_type | table | type | possible_keys |
++----+-------------+-------+-------+---------------+
+| 1 | SIMPLE | users | const | PRIMARY |
++----+-------------+-------+-------+---------------+
++---------+---------+-------+------+-------+
+| key | key_len | ref | rows | Extra |
++---------+---------+-------+------+-------+
+| PRIMARY | 4 | const | 1 | |
++---------+---------+-------+------+-------+
+
1 row in set (0.00 sec)
EXPLAIN for: SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN (1)
-+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
-| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
-| 1 | SIMPLE | articles | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
-+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
++----+-------------+----------+------+---------------+
+| id | select_type | table | type | possible_keys |
++----+-------------+----------+------+---------------+
+| 1 | SIMPLE | articles | ALL | NULL |
++----+-------------+----------+------+---------------+
++------+---------+------+------+-------------+
+| key | key_len | ref | rows | Extra |
++------+---------+------+------+-------------+
+| NULL | NULL | NULL | 1 | Using where |
++------+---------+------+------+-------------+
+
+
1 row in set (0.00 sec)
```
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 0061c83a0c..6efc64c385 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -378,13 +378,13 @@ About your application's environment
Ruby version 1.9.3 (x86_64-linux)
RubyGems version 1.3.6
Rack version 1.3
-Rails version 4.1.0
+Rails version 4.1.1
JavaScript Runtime Node.js (V8)
-Active Record version 4.1.0
-Action Pack version 4.1.0
-Action View version 4.1.0
-Action Mailer version 4.1.0
-Active Support version 4.1.0
+Active Record version 4.1.1
+Action Pack version 4.1.1
+Action View version 4.1.1
+Action Mailer version 4.1.1
+Active Support version 4.1.1
Middleware Rack::Sendfile, ActionDispatch::Static, Rack::Lock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007ffd131a7c88>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::Head, Rack::ConditionalGet, Rack::ETag
Application root /home/foobar/commandsapp
Environment development
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 2dd00bed2e..7a9e1beb23 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -729,13 +729,47 @@ Rails will now prepend "/app1" when generating links.
#### Using Passenger
-Passenger makes it easy to run your application in a subdirectory. You can find
-the relevant configuration in the
-[passenger manual](http://www.modrails.com/documentation/Users%20guide%20Apache.html#deploying_rails_to_sub_uri).
+Passenger makes it easy to run your application in a subdirectory. You can find the relevant configuration in the [passenger manual](http://www.modrails.com/documentation/Users%20guide%20Apache.html#deploying_rails_to_sub_uri).
#### Using a Reverse Proxy
-TODO
+Deploying your application using a reverse proxy has definite advantages over traditional deploys. They allow you to have more control over your server by layering the components required by your application.
+
+Many modern web servers can be used as a proxy server to balance third-party elements such as caching servers or application servers.
+
+One such application server you can use is [Unicorn](http://unicorn.bogomips.org/) to run behind a reverse proxy.
+
+In this case, you would need to configure the proxy server (nginx, apache, etc) to accept connections from your application server (Unicorn). By default Unicorn will listen for TCP connections on port 8080, but you can change the port or configure it to use sockets instead.
+
+You can find more information in the [Unicorn readme](http://unicorn.bogomips.org/README.html) and understand the [philosophy](http://unicorn.bogomips.org/PHILOSOPHY.html) behind it.
+
+Once you've configured the application server, you must proxy requests to it by configuring your web server appropriately. For example your nginx config may include:
+
+```
+upstream application_server {
+ server 0.0.0.0:8080
+}
+
+server {
+ listen 80;
+ server_name localhost;
+
+ root /root/path/to/your_app/public;
+
+ try_files $uri/index.html $uri.html @app;
+
+ location @app {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $http_host;
+ proxy_redirect off;
+ proxy_pass http://application_server;
+ }
+
+ # some other configuration
+}
+```
+
+Be sure to read the [nginx documentation](http://nginx.org/en/docs/) for the most up-to-date information.
#### Considerations when deploying to a subdirectory
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index d3a96daf7b..133ef58fd6 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -215,6 +215,36 @@ Rails follows a simple set of coding style conventions:
The above are guidelines - please use your best judgment in using them.
+### Benchmark Your Code
+
+If your change has an impact on the performance of Rails, please use the
+[benchmark-ips](https://github.com/evanphx/benchmark-ips) gem to provide
+benchmark results for comparison.
+
+Here's an example of using benchmark-ips:
+
+```ruby
+require 'benchmark/ips'
+
+Benchmark.ips do |x|
+ x.report('addition') { 1 + 2 }
+ x.report('addition with send') { 1.send(:+, 2) }
+end
+```
+
+This will generate a report with the following information:
+
+```
+Calculating -------------------------------------
+ addition 69114 i/100ms
+ addition with send 64062 i/100ms
+-------------------------------------------------
+ addition 5307644.4 (±3.5%) i/s - 26539776 in 5.007219s
+ addition with send 3702897.9 (±3.5%) i/s - 18513918 in 5.006723s
+```
+
+Please see the benchmark/ips [README](https://github.com/evanphx/benchmark-ips/blob/master/README.md) for more information.
+
### Running Tests
It is not customary in Rails to run the full test suite before pushing
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index e79ebae818..5f738b76af 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -308,7 +308,7 @@ For example:
```bash
=> Booting WEBrick
-=> Rails 4.1.0 application starting in development on http://0.0.0.0:3000
+=> Rails 4.1.1 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
=> Ctrl-C to shutdown server
@@ -421,11 +421,11 @@ then `backtrace` will supply the answer.
--> #0 ArticlesController.index
at /PathTo/project/test_app/app/controllers/articles_controller.rb:8
#1 ActionController::ImplicitRender.send_action(method#String, *args#Array)
- at /PathToGems/actionpack-4.1.0/lib/action_controller/metal/implicit_render.rb:4
+ at /PathToGems/actionpack-4.1.1/lib/action_controller/metal/implicit_render.rb:4
#2 AbstractController::Base.process_action(action#NilClass, *args#Array)
- at /PathToGems/actionpack-4.1.0/lib/abstract_controller/base.rb:189
+ at /PathToGems/actionpack-4.1.1/lib/abstract_controller/base.rb:189
#3 ActionController::Rendering.process_action(action#NilClass, *args#NilClass)
- at /PathToGems/actionpack-4.1.0/lib/action_controller/metal/rendering.rb:10
+ at /PathToGems/actionpack-4.1.1/lib/action_controller/metal/rendering.rb:10
...
```
@@ -437,7 +437,7 @@ context.
```
(byebug) frame 2
-[184, 193] in /PathToGems/actionpack-4.1.0/lib/abstract_controller/base.rb
+[184, 193] in /PathToGems/actionpack-4.1.1/lib/abstract_controller/base.rb
184: # is the intended way to override action dispatching.
185: #
186: # Notice that the first argument is the method to be dispatched
@@ -654,7 +654,7 @@ instruction to be executed. In this case, the activesupport's `week` method.
```
(byebug) step
-[50, 59] in /PathToGems/activesupport-4.1.0/lib/active_support/core_ext/numeric/time.rb
+[50, 59] in /PathToGems/activesupport-4.1.1/lib/active_support/core_ext/numeric/time.rb
50: ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
51: end
52: alias :day :days
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 34ce570545..530232f3f3 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -125,7 +125,7 @@ run the following:
$ bin/rails --version
```
-If it says something like "Rails 4.1.0", you are ready to continue.
+If it says something like "Rails 4.1.1", you are ready to continue.
### Creating the Blog Application
diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md
index 8faf03e58c..f0230b428b 100644
--- a/guides/source/ruby_on_rails_guides_guidelines.md
+++ b/guides/source/ruby_on_rails_guides_guidelines.md
@@ -13,7 +13,7 @@ After reading this guide, you will know:
Markdown
-------
-Guides are written in [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/). There is comprehensive [documentation for Markdown](http://daringfireball.net/projects/markdown/syntax), a [cheatsheet](http://daringfireball.net/projects/markdown/basics), and [additional documentation](http://github.github.com/github-flavored-markdown/) on the differences from traditional Markdown.
+Guides are written in [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown). There is comprehensive [documentation for Markdown](http://daringfireball.net/projects/markdown/syntax), a [cheatsheet](http://daringfireball.net/projects/markdown/basics).
Prologue
--------
diff --git a/guides/source/testing.md b/guides/source/testing.md
index e9a5e0d7ab..4149146c4c 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -699,8 +699,6 @@ A simple integration test that exercises multiple controllers:
require 'test_helper'
class UserFlowsTest < ActionDispatch::IntegrationTest
- fixtures :users
-
test "login and browse site" do
# login via https
https!
@@ -727,10 +725,7 @@ Here's an example of multiple sessions and custom DSL in an integration test
require 'test_helper'
class UserFlowsTest < ActionDispatch::IntegrationTest
- fixtures :users
-
test "login and browse site" do
-
# User david logs in
david = login(:david)
# User guest logs in
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index ba6a0feeef..1343f0a628 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,9 +1,12 @@
+* Replace double quotes with single quotes while adding an entry into Gemfile.
+
+ *Alexander Belaev*
+
* Default `config.assets.digest` to `true` in development.
*Dan Kang*
-* Load database configuration from the first
- database.yml available in paths.
+* Load database configuration from the first `database.yml` available in paths.
*Pier-Olivier Thibault*
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index dce734b54e..04ce38f841 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -156,7 +156,8 @@ module Rails
args << "--help" if args.empty? && klass.arguments.any? { |a| a.required? }
klass.start(args, config)
else
- puts "Could not find generator #{namespace}."
+ puts "Could not find generator '#{namespace}'. Please choose a generator below."
+ print_generators
end
end
@@ -199,17 +200,6 @@ module Rails
# Show help message with available generators.
def self.help(command = 'generate')
- lookup!
-
- namespaces = subclasses.map{ |k| k.namespace }
- namespaces.sort!
-
- groups = Hash.new { |h,k| h[k] = [] }
- namespaces.each do |namespace|
- base = namespace.split(':').first
- groups[base] << namespace
- end
-
puts "Usage: rails #{command} GENERATOR [args] [options]"
puts
puts "General options:"
@@ -222,6 +212,20 @@ module Rails
puts "Please choose a generator below."
puts
+ print_generators
+ end
+
+ def self.print_generators
+ lookup!
+
+ namespaces = subclasses.map{ |k| k.namespace }
+ namespaces.sort!
+
+ groups = Hash.new { |h,k| h[k] = [] }
+ namespaces.each do |namespace|
+ base = namespace.split(':').first
+ groups[base] << namespace
+ end
# Print Rails defaults first.
rails = groups.delete("rails")
rails.map! { |n| n.sub(/^rails:/, '') }
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 625f031c94..a239874df0 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -20,9 +20,9 @@ module Rails
# Set the message to be shown in logs. Uses the git repo if one is given,
# otherwise use name (version).
- parts, message = [ name.inspect ], name
+ parts, message = [ quote(name) ], name
if version ||= options.delete(:version)
- parts << version.inspect
+ parts << quote(version)
message << " (#{version})"
end
message = options[:git] if options[:git]
@@ -30,7 +30,7 @@ module Rails
log :gemfile, message
options.each do |option, value|
- parts << "#{option}: #{value.inspect}"
+ parts << "#{option}: #{quote(value)}"
end
in_root do
@@ -68,7 +68,7 @@ module Rails
log :source, source
in_root do
- prepend_file "Gemfile", "source #{source.inspect}\n", verbose: false
+ prepend_file "Gemfile", "source #{quote(source)}\n", verbose: false
end
end
@@ -255,6 +255,15 @@ module Rails
end
end
+ # Surround string with single quotes if there is no quotes.
+ # Otherwise fall back to double quotes
+ def quote(str)
+ if str.include?("'")
+ str.inspect
+ else
+ "'#{str}'"
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index 5a92ab3e95..b7da44ca2d 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -30,7 +30,12 @@ module Rails
protected
attr_reader :file_name
- alias :singular_name :file_name
+
+ # FIXME: We are avoiding to use alias because a bug on thor that make
+ # this method public and add it to the task list.
+ def singular_name
+ file_name
+ end
# Wrap block with namespace of current application
# if namespace exists and is not skipped
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 448b6f4845..5bdbd58097 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -16,7 +16,7 @@ source 'https://rubygems.org'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
-# Use unicorn as the app server
+# Use Unicorn as the app server
# gem 'unicorn'
# Use Capistrano for deployment
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
index 6b011e577a..87b8fe3516 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
@@ -5,9 +5,6 @@ require 'rails/test_help'
class ActiveSupport::TestCase
<% unless options[:skip_active_record] -%>
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
- #
- # Note: You'll currently still have to declare fixtures explicitly in integration tests
- # -- they do not yet inherit this setting
fixtures :all
<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile
index 0ba899176c..c338a0bdb1 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile
@@ -19,6 +19,10 @@ APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
<% end %>
+<% if engine? -%>
+load 'rails/tasks/statistics.rake'
+<% end %>
+
<% unless options[:skip_gemspec] -%>
Bundler::GemHelper.install_tasks
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
index 908c4ce65e..49e5431a16 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -5,7 +5,7 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
layout -> { request.xhr? ? false : 'application' }
- before_filter :require_local!
+ before_action :require_local!
def index
redirect_to action: :routes
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index dd318f52e5..32740d66da 100644
--- a/railties/lib/rails/mailers_controller.rb
+++ b/railties/lib/rails/mailers_controller.rb
@@ -3,8 +3,8 @@ require 'rails/application_controller'
class Rails::MailersController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
- before_filter :require_local!
- before_filter :find_preview, only: :preview
+ before_action :require_local!
+ before_action :find_preview, only: :preview
def index
@previews = ActionMailer::Preview.all
@@ -70,4 +70,4 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
@email
end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
index 3b7f358a5b..df74643a59 100644
--- a/railties/lib/rails/ruby_version_check.rb
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -2,7 +2,7 @@ if RUBY_VERSION < '1.9.3'
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
- Rails 4 prefers to run on Ruby 2.0.
+ Rails 4 prefers to run on Ruby 2.1 or newer.
You're running
#{desc}
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index c1674c72ad..ae5a7d2759 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -1,3 +1,6 @@
+# while having global constant is not good,
+# many 3rd party tools depend on it, like rspec-rails, cucumber-rails, etc
+# so if will be removed - deprecation warning is needed
STATS_DIRECTORIES = [
%w(Controllers app/controllers),
%w(Helpers app/helpers),
@@ -13,10 +16,12 @@ STATS_DIRECTORIES = [
%w(Integration\ tests test/integration),
%w(Functional\ tests\ (old) test/functional),
%w(Unit\ tests \ (old) test/unit)
-].collect { |name, dir| [ name, "#{Rails.root}/#{dir}" ] }.select { |name, dir| File.directory?(dir) }
+].collect do |name, dir|
+ [ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
+end.select { |name, dir| File.directory?(dir) }
-desc "Report code statistics (KLOCs, etc) from the application"
+desc "Report code statistics (KLOCs, etc) from the application or engine"
task :stats do
require 'rails/code_statistics'
CodeStatistics.new(*STATS_DIRECTORIES).to_s
-end
+end \ No newline at end of file
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 0db40c1d32..6d6de0fb52 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -41,13 +41,13 @@ class ActionsTest < Rails::Generators::TestCase
def test_add_source_adds_source_to_gemfile
run_generator
action :add_source, 'http://gems.github.com'
- assert_file 'Gemfile', /source "http:\/\/gems\.github\.com"/
+ assert_file 'Gemfile', /source 'http:\/\/gems\.github\.com'/
end
def test_gem_should_put_gem_dependency_in_gemfile
run_generator
action :gem, 'will-paginate'
- assert_file 'Gemfile', /gem "will\-paginate"/
+ assert_file 'Gemfile', /gem 'will\-paginate'/
end
def test_gem_with_version_should_include_version_in_gemfile
@@ -55,7 +55,7 @@ class ActionsTest < Rails::Generators::TestCase
action :gem, 'rspec', '>=2.0.0.a5'
- assert_file 'Gemfile', /gem "rspec", ">=2.0.0.a5"/
+ assert_file 'Gemfile', /gem 'rspec', '>=2.0.0.a5'/
end
def test_gem_should_insert_on_separate_lines
@@ -66,8 +66,8 @@ class ActionsTest < Rails::Generators::TestCase
action :gem, 'rspec'
action :gem, 'rspec-rails'
- assert_file 'Gemfile', /^gem "rspec"$/
- assert_file 'Gemfile', /^gem "rspec-rails"$/
+ assert_file 'Gemfile', /^gem 'rspec'$/
+ assert_file 'Gemfile', /^gem 'rspec-rails'$/
end
def test_gem_should_include_options
@@ -75,7 +75,15 @@ class ActionsTest < Rails::Generators::TestCase
action :gem, 'rspec', github: 'dchelimsky/rspec', tag: '1.2.9.rc1'
- assert_file 'Gemfile', /gem "rspec", github: "dchelimsky\/rspec", tag: "1\.2\.9\.rc1"/
+ assert_file 'Gemfile', /gem 'rspec', github: 'dchelimsky\/rspec', tag: '1\.2\.9\.rc1'/
+ end
+
+ def test_gem_falls_back_to_inspect_if_string_contains_single_quote
+ run_generator
+
+ action :gem, 'rspec', ">=2.0'0"
+
+ assert_file 'Gemfile', /^gem 'rspec', ">=2\.0'0"$/
end
def test_gem_group_should_wrap_gems_in_a_group
@@ -89,7 +97,7 @@ class ActionsTest < Rails::Generators::TestCase
gem 'fakeweb'
end
- assert_file 'Gemfile', /\ngroup :development, :test do\n gem "rspec-rails"\nend\n\ngroup :test do\n gem "fakeweb"\nend/
+ assert_file 'Gemfile', /\ngroup :development, :test do\n gem 'rspec-rails'\nend\n\ngroup :test do\n gem 'fakeweb'\nend/
end
def test_environment_should_include_data_in_environment_initializer_block
diff --git a/railties/test/generators/argv_scrubber_test.rb b/railties/test/generators/argv_scrubber_test.rb
index 31e07bc8da..31c2d846e2 100644
--- a/railties/test/generators/argv_scrubber_test.rb
+++ b/railties/test/generators/argv_scrubber_test.rb
@@ -1,5 +1,5 @@
-require 'active_support/test_case'
require 'active_support/testing/autorun'
+require 'active_support/test_case'
require 'rails/generators/rails/app/app_generator'
require 'tempfile'
diff --git a/railties/test/generators/generator_test.rb b/railties/test/generators/generator_test.rb
index 7871399dd7..b136239795 100644
--- a/railties/test/generators/generator_test.rb
+++ b/railties/test/generators/generator_test.rb
@@ -1,5 +1,5 @@
-require 'active_support/test_case'
require 'active_support/testing/autorun'
+require 'active_support/test_case'
require 'rails/generators/app_base'
module Rails
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index eac28badfe..8d6dbf80c2 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -21,8 +21,10 @@ class GeneratorsTest < Rails::Generators::TestCase
end
def test_invoke_when_generator_is_not_found
- output = capture(:stdout){ Rails::Generators.invoke :unknown }
- assert_equal "Could not find generator unknown.\n", output
+ name = :unknown
+ output = capture(:stdout){ Rails::Generators.invoke name }
+ assert_match "Could not find generator '#{name}'", output
+ assert_match "scaffold", output
end
def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments