aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--RELEASING_RAILS.rdoc3
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer/base.rb2
-rw-r--r--actionpack/CHANGELOG.md53
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb150
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb3
-rw-r--r--actionpack/lib/action_controller/caching.rb3
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb2
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb4
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb10
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb12
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb4
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/filter_redirect.rb37
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb2
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb38
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb138
-rw-r--r--actionpack/lib/action_view/helpers/asset_url_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/benchmark_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb91
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb214
-rw-r--r--actionpack/lib/action_view/helpers/output_safety_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb2
-rw-r--r--actionpack/lib/action_view/railtie.rb2
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb11
-rw-r--r--actionpack/test/abstract/callbacks_test.rb92
-rw-r--r--actionpack/test/controller/flash_test.rb6
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb6
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb4
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb6
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb88
-rw-r--r--actionpack/test/controller/mime_responds_test.rb2
-rw-r--r--actionpack/test/controller/new_base/base_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_context_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_template_test.rb10
-rw-r--r--actionpack/test/controller/render_test.rb6
-rw-r--r--actionpack/test/controller/rescue_test.rb4
-rw-r--r--actionpack/test/controller/show_exceptions_test.rb2
-rw-r--r--actionpack/test/controller/view_paths_test.rb2
-rw-r--r--actionpack/test/dispatch/routing_test.rb29
-rw-r--r--actionpack/test/template/render_test.rb5
-rw-r--r--actionpack/test/template/template_test.rb4
-rw-r--r--activemodel/lib/active_model/errors.rb6
-rw-r--r--activemodel/lib/active_model/serialization.rb2
-rw-r--r--activemodel/lib/active_model/validator.rb4
-rw-r--r--activemodel/test/cases/errors_test.rb5
-rw-r--r--activerecord/CHANGELOG.md80
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb6
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb16
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/counter_cache.rb18
-rw-r--r--activerecord/lib/active_record/integration.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb24
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/querying.rb7
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb9
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb6
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb90
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb10
-rw-r--r--activerecord/lib/active_record/schema.rb47
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb21
-rw-r--r--activerecord/lib/active_record/schema_migration.rb39
-rw-r--r--activerecord/lib/active_record/scoping/named.rb4
-rw-r--r--activerecord/test/cases/ar_schema_test.rb51
-rw-r--r--activerecord/test/cases/associations/eager_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/migration/logger_test.rb2
-rw-r--r--activerecord/test/cases/migration/table_and_index_test.rb24
-rw-r--r--activerecord/test/cases/migration_test.rb11
-rw-r--r--activerecord/test/cases/migrator_test.rb9
-rw-r--r--activerecord/test/cases/persistence_test.rb16
-rw-r--r--activerecord/test/cases/relation/where_chain_test.rb75
-rw-r--r--activerecord/test/cases/relation/where_test.rb6
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb17
-rw-r--r--activerecord/test/cases/schema_migration_test.rb54
-rw-r--r--activerecord/test/migrations/always_safe/1001_always_safe.rb5
-rw-r--r--activerecord/test/migrations/always_safe/1002_still_safe.rb5
-rw-r--r--activerecord/test/models/developer.rb10
-rw-r--r--activesupport/CHANGELOG.md6
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/basic_object.rb16
-rw-r--r--activesupport/lib/active_support/cache.rb2
-rw-r--r--activesupport/lib/active_support/callbacks.rb4
-rw-r--r--activesupport/lib/active_support/configurable.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/object/deep_dup.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/marshal.rb2
-rw-r--r--activesupport/lib/active_support/dependencies.rb6
-rw-r--r--activesupport/lib/active_support/duration.rb4
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb2
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb8
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb4
-rw-r--r--activesupport/lib/active_support/message_verifier.rb2
-rw-r--r--activesupport/lib/active_support/multibyte.rb2
-rw-r--r--activesupport/lib/active_support/proxy_object.rb13
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb2
-rw-r--r--activesupport/lib/active_support/test_case.rb2
-rw-r--r--activesupport/lib/active_support/testing/constant_lookup.rb2
-rw-r--r--activesupport/lib/active_support/testing/pending.rb4
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb8
-rw-r--r--activesupport/test/core_ext/duration_test.rb4
-rw-r--r--activesupport/test/dependencies_test.rb10
-rw-r--r--activesupport/test/multibyte_chars_test.rb3
-rw-r--r--activesupport/test/time_zone_test.rb7
-rw-r--r--guides/assets/images/rails4_features.pngbin0 -> 132154 bytes
-rw-r--r--guides/rails_guides/generator.rb2
-rw-r--r--guides/source/4_0_release_notes.md867
-rw-r--r--guides/source/_welcome.html.erb2
-rw-r--r--guides/source/action_controller_overview.md47
-rw-r--r--guides/source/action_mailer_basics.md8
-rw-r--r--guides/source/action_view_overview.md2
-rw-r--r--guides/source/active_model_basics.md88
-rw-r--r--guides/source/active_record_basics.md42
-rw-r--r--guides/source/active_record_callbacks.md2
-rw-r--r--guides/source/active_record_querying.md14
-rw-r--r--guides/source/active_record_validations.md42
-rw-r--r--guides/source/caching_with_rails.md2
-rw-r--r--guides/source/getting_started.md20
-rw-r--r--guides/source/i18n.md10
-rw-r--r--guides/source/migrations.md584
-rw-r--r--guides/source/performance_testing.md4
-rw-r--r--guides/source/routing.md206
-rw-r--r--guides/source/ruby_on_rails_guides_guidelines.md4
-rw-r--r--guides/source/security.md2
-rw-r--r--railties/CHANGELOG.md6
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/application/configuration.rb3
-rw-r--r--railties/lib/rails/commands.rb2
-rw-r--r--railties/lib/rails/commands/console.rb2
-rw-r--r--railties/lib/rails/commands/profiler.rb2
-rw-r--r--railties/lib/rails/commands/runner.rb2
-rw-r--r--railties/lib/rails/commands/server.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/gitignore2
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb12
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb2
-rw-r--r--railties/test/abstract_unit.rb1
-rw-r--r--railties/test/application/rake/dbs_test.rb30
-rw-r--r--railties/test/application/rake/notes_test.rb8
-rw-r--r--railties/test/application/runner_test.rb18
-rw-r--r--railties/test/commands/console_test.rb23
-rw-r--r--railties/test/commands/server_test.rb16
-rw-r--r--railties/test/env_helpers.rb26
-rw-r--r--railties/test/generators/actions_test.rb31
-rw-r--r--railties/test/generators/scaffold_generator_test.rb28
170 files changed, 2083 insertions, 2095 deletions
diff --git a/.travis.yml b/.travis.yml
index 7e3d728872..18b0100c62 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,7 +22,7 @@ notifications:
on_success: change
on_failure: always
rooms:
- - secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
+ - secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI="
bundler_args: --path vendor/bundle
matrix:
allow_failures:
diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc
index af1def223a..9af79f73e2 100644
--- a/RELEASING_RAILS.rdoc
+++ b/RELEASING_RAILS.rdoc
@@ -164,6 +164,9 @@ Today, do this stuff in this order:
* Update RAILS_VERSION to remove the rc
* Build and test the gem
* Release the gems
+* If releasing a new stable version:
+ - Trigger stable docs generation (see below)
+ - Update the version in the home page
* Email security lists
* Email general announcement lists
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index e9f979f34b..67ec0d1097 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -21,5 +21,5 @@ Gem::Specification.new do |s|
s.add_dependency 'actionpack', version
- s.add_dependency 'mail', '~> 2.5.2'
+ s.add_dependency 'mail', '~> 2.5.3'
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index c9f10b359b..31005c9d9b 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -20,8 +20,6 @@ module ActionMailer
# used to generate an email message. In these methods, you can setup variables to be used in
# the mailer views, options on the mail itself such as the <tt>:from</tt> address, and attachments.
#
- # Examples:
- #
# class Notifier < ActionMailer::Base
# default from: 'no-reply@example.com',
# return_path: 'system@example.com'
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index e5bd21ea7b..9db30c8cf7 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,58 @@
## Rails 4.0.0 (unreleased) ##
+* Rename all action callbacks from *_filter to *_action to avoid the misconception that these
+ callbacks are only suited for transforming or halting the response. With the new style,
+ it's more inviting to use them as they were intended, like setting shared ivars for views.
+
+ Example:
+
+ class PeopleController < ActionController::Base
+ before_action :set_person, except: [ :index, :new, :create ]
+ before_action :ensure_permission, only: [ :edit, :update ]
+
+ ...
+
+ private
+ def set_person
+ @person = current_account.people.find(params[:id])
+ end
+
+ def ensure_permission
+ current_person.can_change?(@person)
+ end
+ end
+
+ The old *_filter methods still work with no deprecation notice.
+
+ *DHH*
+
+* Add :if / :unless conditions to fragment cache:
+
+ <%= cache @model, if: some_condition(@model) do %>
+
+ *Stephen Ausman + Fabrizio Regini*
+
+* Add filter capability to ActionController logs for redirect locations:
+
+ config.filter_redirect << 'http://please.hide.it/'
+
+ *Fabrizio Regini*
+
+* Fixed a bug that ignores constraints on a glob route. This was caused because the constraint
+ regular expression is overwritten when the `routes.rb` file is processed. Fixes #7924
+
+ *Maura Fitzgerald*
+
+* More descriptive error messages when calling `render :partial` with
+ an invalid `:layout` argument.
+ #8376
+
+ render :partial => 'partial', :layout => true
+
+ # results in ActionView::MissingTemplate: Missing partial /true
+
+ *Yves Senn*
+
* Sweepers was extracted from Action Controller as `rails-observers` gem.
*Rafael Mendonça França*
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 02ac111392..599fff81c2 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -40,19 +40,22 @@ module AbstractController
end
end
- # Skip before, after, and around filters matching any of the names
+ # Skip before, after, and around action callbacks matching any of the names
+ # Aliased as skip_filter.
#
# ==== 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
- def skip_filter(*names)
- skip_before_filter(*names)
- skip_after_filter(*names)
- skip_around_filter(*names)
+ 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
+
# Take callback names and an optional callback proc, normalize them,
# then call the block with each callback. This allows us to abstract
# the normalization across several methods that use it.
@@ -75,119 +78,138 @@ module AbstractController
end
##
- # :method: before_filter
+ # :method: before_action
#
- # :call-seq: before_filter(names, block)
+ # :call-seq: before_action(names, block)
#
- # Append a before filter. See _insert_callbacks for parameter details.
+ # Append a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as before_filter.
##
- # :method: prepend_before_filter
+ # :method: prepend_before_action
#
- # :call-seq: prepend_before_filter(names, block)
+ # :call-seq: prepend_before_action(names, block)
#
- # Prepend a before filter. See _insert_callbacks for parameter details.
+ # Prepend a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as prepend_before_filter.
##
- # :method: skip_before_filter
+ # :method: skip_before_action
#
- # :call-seq: skip_before_filter(names)
+ # :call-seq: skip_before_action(names)
#
- # Skip a before filter. See _insert_callbacks for parameter details.
+ # Skip a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as skip_before_filter.
##
- # :method: append_before_filter
+ # :method: append_before_action
#
- # :call-seq: append_before_filter(names, block)
+ # :call-seq: append_before_action(names, block)
#
- # Append a before filter. See _insert_callbacks for parameter details.
+ # Append a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as append_before_filter.
##
- # :method: after_filter
+ # :method: after_action
#
- # :call-seq: after_filter(names, block)
+ # :call-seq: after_action(names, block)
#
- # Append an after filter. See _insert_callbacks for parameter details.
+ # Append a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as after_filter.
##
- # :method: prepend_after_filter
+ # :method: prepend_after_action
#
- # :call-seq: prepend_after_filter(names, block)
+ # :call-seq: prepend_after_action(names, block)
#
- # Prepend an after filter. See _insert_callbacks for parameter details.
+ # Prepend a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as prepend_after_filter.
##
- # :method: skip_after_filter
+ # :method: skip_after_action
#
- # :call-seq: skip_after_filter(names)
+ # :call-seq: skip_after_action(names)
#
- # Skip an after filter. See _insert_callbacks for parameter details.
+ # Skip a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as skip_after_filter.
##
- # :method: append_after_filter
+ # :method: append_after_action
#
- # :call-seq: append_after_filter(names, block)
+ # :call-seq: append_after_action(names, block)
#
- # Append an after filter. See _insert_callbacks for parameter details.
+ # Append a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as append_after_filter.
##
- # :method: around_filter
+ # :method: around_action
#
- # :call-seq: around_filter(names, block)
+ # :call-seq: around_action(names, block)
#
- # Append an around filter. See _insert_callbacks for parameter details.
+ # Append a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as around_filter.
##
- # :method: prepend_around_filter
+ # :method: prepend_around_action
#
- # :call-seq: prepend_around_filter(names, block)
+ # :call-seq: prepend_around_action(names, block)
#
- # Prepend an around filter. See _insert_callbacks for parameter details.
+ # Prepend a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as prepend_around_filter.
##
- # :method: skip_around_filter
+ # :method: skip_around_action
#
- # :call-seq: skip_around_filter(names)
+ # :call-seq: skip_around_action(names)
#
- # Skip an around filter. See _insert_callbacks for parameter details.
+ # Skip a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as skip_around_filter.
##
- # :method: append_around_filter
+ # :method: append_around_action
#
- # :call-seq: append_around_filter(names, block)
+ # :call-seq: append_around_action(names, block)
#
- # Append an around filter. See _insert_callbacks for parameter details.
+ # Append a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as append_around_filter.
- # set up before_filter, prepend_before_filter, skip_before_filter, etc.
+ # set up before_action, prepend_before_action, skip_before_action, etc.
# for each of before, after, and around.
- [:before, :after, :around].each do |filter|
+ [:before, :after, :around].each do |callback|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- # Append a before, after or around filter. See _insert_callbacks
+ # Append a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
- def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
- end # end
- end # end
+ def #{callback}_action(*names, &blk) # def before_action(*names, &blk)
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
+ set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options)
+ end # end
+ end # end
+
+ alias_method :#{callback}_filter, :#{callback}_action
- # Prepend a before, after or around filter. See _insert_callbacks
+ # Prepend a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
- def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
- end # end
- end # end
+ def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk)
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
+ set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
+ end # end
+ end # end
+
+ alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action
- # Skip a before, after or around filter. See _insert_callbacks
+ # Skip a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
- def skip_#{filter}_filter(*names) # def skip_before_filter(*names)
- _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
- skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
- end # end
- end # end
-
- # *_filter is the same as append_*_filter
- alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter
+ def skip_#{callback}_action(*names) # def skip_before_action(*names)
+ _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
+ skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options)
+ end # end
+ end # end
+
+ alias_method :skip_#{callback}_filter, :skip_#{callback}_action
+
+ # *_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
RUBY_EVAL
end
end
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index d3929b685c..d4e73bf257 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -58,11 +58,10 @@ module AbstractController
# The +helper+ class method can take a series of helper module names, a block, or both.
#
- # ==== Parameters
+ # ==== Options
# * <tt>*args</tt> - Module, Symbol, String, :all
# * <tt>block</tt> - A block defining helper methods
#
- # ==== Examples
# When the argument is a module it will be included directly in the template class.
# helper FooHelper # => includes FooHelper
#
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 177da1c8a0..2892e093af 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -6,8 +6,7 @@ module ActionController
# \Caching is a cheap way of speeding up slow applications by keeping the result of
# calculations, renderings, and database calls around for subsequent requests.
#
- # You can read more about each approach and the by clicking the
- # modules below.
+ # You can read more about each approach by clicking the modules below.
#
# Note: To turn off all caching, set
# config.action_controller.perform_caching = false.
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 426adfe675..3f9b382a11 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -42,7 +42,7 @@ module ActionController
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
# +true+ if you want your application to be cachable by other devices (proxy caches).
#
- # === Example:
+ # === Example:
#
# def show
# @article = Article.find(params[:id])
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index c38d8ccef3..f1e8714a86 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -32,14 +32,14 @@ module ActionController
# ==== Options
# * <tt>host</tt> - Redirect to a different host name
# * <tt>only</tt> - The callback should be run only for this action
- # * <tt>except</tt> - The callback should be run for all actions except this action
+ # * <tt>except</tt> - The callback should be run for all actions except this action
# * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
# will be called only when it returns a true value.
# * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
# will be called only when it returns a false value.
def force_ssl(options = {})
host = options.delete(:host)
- before_filter(options) do
+ before_action(options) do
force_ssl_redirect(host)
end
end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index d3b5bafee1..283f6413ec 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -25,7 +25,7 @@ module ActionController
# the regular HTML interface is protected by a session approach:
#
# class ApplicationController < ActionController::Base
- # before_filter :set_account, :authenticate
+ # before_action :set_account, :authenticate
#
# protected
# def set_account
@@ -68,7 +68,7 @@ module ActionController
module ClassMethods
def http_basic_authenticate_with(options = {})
- before_filter(options.except(:name, :password, :realm)) do
+ before_action(options.except(:name, :password, :realm)) do
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
name == options[:name] && password == options[:password]
end
@@ -124,7 +124,7 @@ module ActionController
# USERS = {"dhh" => "secret", #plain text password
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
#
- # before_filter :authenticate, except: [:index]
+ # before_action :authenticate, except: [:index]
#
# def index
# render text: "Everyone can see me!"
@@ -317,7 +317,7 @@ module ActionController
# class PostsController < ApplicationController
# TOKEN = "secret"
#
- # before_filter :authenticate, except: [ :index ]
+ # before_action :authenticate, except: [ :index ]
#
# def index
# render text: "Everyone can see me!"
@@ -340,7 +340,7 @@ module ActionController
# the regular HTML interface is protected by a session approach:
#
# class ApplicationController < ActionController::Base
- # before_filter :set_account, :authenticate
+ # before_action :set_account, :authenticate
#
# protected
# def set_account
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index ca4ae532ca..d3aa8f90c5 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -60,7 +60,7 @@ module ActionController
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
result = super
payload[:status] = response.status
- payload[:location] = response.location
+ payload[:location] = response.filtered_location
result
end
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 265ce5d6f3..c5db0cb0d4 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -19,7 +19,7 @@ module ActionController #:nodoc:
#
# class ApplicationController < ActionController::Base
# protect_from_forgery
- # skip_before_filter :verify_authenticity_token, if: :json_request?
+ # skip_before_action :verify_authenticity_token, if: :json_request?
#
# protected
#
@@ -66,15 +66,15 @@ module ActionController #:nodoc:
#
# You can disable csrf protection on controller-by-controller basis:
#
- # skip_before_filter :verify_authenticity_token
+ # skip_before_action :verify_authenticity_token
#
# It can also be disabled for specific controller actions:
#
- # skip_before_filter :verify_authenticity_token, except: [:create]
+ # skip_before_action :verify_authenticity_token, except: [:create]
#
# Valid Options:
#
- # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified.
# * <tt>:with</tt> - Set the method to handle unverified request.
#
# Valid unverified request handling methods are:
@@ -84,7 +84,7 @@ module ActionController #:nodoc:
def protect_from_forgery(options = {})
include protection_method_module(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
- prepend_before_filter :verify_authenticity_token, options
+ prepend_before_action :verify_authenticity_token, options
end
private
@@ -152,7 +152,7 @@ module ActionController #:nodoc:
end
protected
- # The actual before_filter that is used. Modify this to change how you handle unverified requests.
+ # The actual before_action that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
unless verified_request?
logger.warn "Can't verify CSRF token authenticity" if logger
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 4eb582648e..0b3c438ec2 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -21,8 +21,6 @@ module ActionController #:nodoc:
# supports fibers (fibers are supported since version 1.9.2 of the main
# Ruby implementation).
#
- # == Examples
- #
# Streaming can be added to a given template easily, all you need to do is
# to pass the :stream option.
#
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 386075bd30..25e72adbe0 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -163,7 +163,7 @@ module ActionController
# }
# })
#
- # permitted = params.permit(person: [ :name, { pets: :name } ])
+ # permitted = params.permit(person: [ :name, { pets: :name } ])
# permitted.permitted? # => true
# permitted[:person][:name] # => "Francesco"
# permitted[:person][:age] # => nil
@@ -228,7 +228,7 @@ module ActionController
# Returns a parameter for the given +key+. If not found,
# returns +nil+.
#
- # params = ActionController::Parameters.new(person: { name: 'Francesco' })
+ # params = ActionController::Parameters.new(person: { name: 'Francesco' })
# params[:person] # => {"name"=>"Francesco"}
# params[:none] # => nil
def [](key)
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 1d716a3248..d002babee3 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -75,6 +75,7 @@ module ActionDispatch
autoload :Parameters
autoload :ParameterFilter
autoload :FilterParameters
+ autoload :FilterRedirect
autoload :Upload
autoload :UploadedFile, 'action_dispatch/http/upload'
autoload :URL
diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb
new file mode 100644
index 0000000000..900ce1c646
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb
@@ -0,0 +1,37 @@
+module ActionDispatch
+ module Http
+ module FilterRedirect
+
+ FILTERED = '[FILTERED]'.freeze # :nodoc:
+
+ def filtered_location
+ if !location_filter.empty? && location_filter_match?
+ FILTERED
+ else
+ location
+ end
+ end
+
+ private
+
+ def location_filter
+ if request.present?
+ request.env['action_dispatch.redirect_filter'] || []
+ else
+ []
+ end
+ end
+
+ def location_filter_match?
+ location_filter.any? do |filter|
+ if String === filter
+ location.include?(filter)
+ elsif Regexp === filter
+ location.match(filter)
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 0f98e84788..57660e93c4 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -68,7 +68,7 @@ module ActionDispatch
# that are not controlled by the extension.
#
# class ApplicationController < ActionController::Base
- # before_filter :adjust_format_for_iphone
+ # before_action :adjust_format_for_iphone
#
# private
# def adjust_format_for_iphone
@@ -87,7 +87,7 @@ module ActionDispatch
# to the :html format.
#
# class ApplicationController < ActionController::Base
- # before_filter :adjust_format_for_iphone_with_html_fallback
+ # before_action :adjust_format_for_iphone_with_html_fallback
#
# private
# def adjust_format_for_iphone_with_html_fallback
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 11b7534ea4..0f808ac9cf 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -61,6 +61,7 @@ module ActionDispatch # :nodoc:
cattr_accessor(:default_headers)
include Rack::Response::Helpers
+ include ActionDispatch::Http::FilterRedirect
include ActionDispatch::Http::Cache::Response
include MonitorMixin
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 7b18c57420..f24e9b8e18 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -105,7 +105,7 @@ module ActionDispatch
super
end
- def []=(k, v) #:nodoc:
+ def []=(k, v)
@discard.delete k
@flashes[k] = v
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 98c87d9b2d..5a835ae439 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -1,7 +1,7 @@
require "action_dispatch"
module ActionDispatch
- class Railtie < Rails::Railtie
+ class Railtie < Rails::Railtie # :nodoc:
config.action_dispatch = ActiveSupport::OrderedOptions.new
config.action_dispatch.x_sendfile_header = nil
config.action_dispatch.ip_spoofing_check = true
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 4417cb841a..d55eb8109a 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -191,8 +191,6 @@ module ActionDispatch
# <tt>:any</tt> which means that the route will respond to any of the HTTP
# methods.
#
- # Examples:
- #
# match 'post/:id' => 'posts#show', via: :get
# match 'post/:id' => 'posts#create_comment', via: :post
#
@@ -204,8 +202,6 @@ module ActionDispatch
# An alternative method of specifying which HTTP method a route should respond to is to use the helper
# methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
#
- # Examples:
- #
# get 'post/:id' => 'posts#show'
# post 'post/:id' => 'posts#create_comment'
#
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 0c19b493ab..3c99932e72 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -158,7 +158,7 @@ module ActionDispatch
def requirements
@requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
- @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
+ @options.each { |k, v| requirements[k] ||= v if v.is_a?(Regexp) }
end
end
@@ -624,8 +624,6 @@ module ActionDispatch
#
# Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
#
- # === Examples
- #
# # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
# scope module: "admin" do
# resources :posts
@@ -706,8 +704,6 @@ module ActionDispatch
# For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
# <tt>Resources#resources</tt>.
#
- # === Examples
- #
# # accessible through /sekret/posts rather than /admin/posts
# namespace :admin, path: "sekret" do
# resources :posts
@@ -1052,15 +1048,7 @@ module ActionDispatch
get :new
end if parent_resource.actions.include?(:new)
- member do
- get :edit if parent_resource.actions.include?(:edit)
- get :show if parent_resource.actions.include?(:show)
- if parent_resource.actions.include?(:update)
- patch :update
- put :update
- end
- delete :destroy if parent_resource.actions.include?(:destroy)
- end
+ set_member_mappings_for_resource
end
self
@@ -1219,15 +1207,7 @@ module ActionDispatch
get :new
end if parent_resource.actions.include?(:new)
- member do
- get :edit if parent_resource.actions.include?(:edit)
- get :show if parent_resource.actions.include?(:show)
- if parent_resource.actions.include?(:update)
- patch :update
- put :update
- end
- delete :destroy if parent_resource.actions.include?(:destroy)
- end
+ set_member_mappings_for_resource
end
self
@@ -1578,6 +1558,18 @@ module ActionDispatch
end
end
end
+
+ def set_member_mappings_for_resource
+ member do
+ get :edit if parent_resource.actions.include?(:edit)
+ get :show if parent_resource.actions.include?(:show)
+ if parent_resource.actions.include?(:update)
+ patch :update
+ put :update
+ end
+ delete :destroy if parent_resource.actions.include?(:destroy)
+ end
+ end
end
# Routing Concerns allow you to declare common routes that can be reused
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index 497ac3d545..6d3f8da932 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -74,8 +74,6 @@ module ActionDispatch
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
# Default is <tt>:url</tt>.
#
- # ==== Examples
- #
# # an Article record
# polymorphic_url(record) # same as article_url(record)
#
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index 2207a43afc..e481f3b245 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -155,8 +155,6 @@ module ActionDispatch
# If the method is called with a block, once all equality tests are
# evaluated the block is called with an array of all matched elements.
#
- # ==== Examples
- #
# # At least one form element
# assert_select "form"
#
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 29a5ccedc1..cf2a117966 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -32,6 +32,9 @@ module ActionView
# You can modify the HTML attributes of the script tag by passing a hash as the
# 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.
+ #
# javascript_include_tag "xmlhr"
# # => <script src="/assets/xmlhr.js?1284139606"></script>
#
@@ -106,19 +109,18 @@ module ActionView
# * <tt>:type</tt> - Override the auto-generated mime type
# * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
#
- # ==== Examples
- # auto_discovery_link_tag
- # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
- # auto_discovery_link_tag(:atom)
- # # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
- # auto_discovery_link_tag(:rss, {action: "feed"})
- # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
- # auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
- # # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
- # auto_discovery_link_tag(:rss, {controller: "news", action: "feed"})
- # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
- # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
- # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
+ # auto_discovery_link_tag
+ # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
+ # auto_discovery_link_tag(:atom)
+ # # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
+ # auto_discovery_link_tag(:rss, {action: "feed"})
+ # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
+ # auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
+ # # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
+ # auto_discovery_link_tag(:rss, {controller: "news", action: "feed"})
+ # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
+ # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
+ # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
if !(type == :rss || type == :atom) && tag_options[:type].blank?
message = "You have passed type other than :rss or :atom to auto_discovery_link_tag and haven't supplied " +
@@ -137,27 +139,24 @@ module ActionView
)
end
- # <%= favicon_link_tag %>
- #
- # generates
- #
- # <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
- #
- # You may specify a different file in the first argument:
+ # Returns a link loading a favicon file. You may specify a different file
+ # in the first argument. The helper accepts an additional options hash where
+ # you can override "rel" and "type".
#
- # <%= favicon_link_tag '/myicon.ico' %>
- #
- # That's passed to +path_to_image+ as is, so it gives
- #
- # <link href="/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
+ # ==== Options
+ # * <tt>:rel</tt> - Specify the relation of this link, defaults to 'shortcut icon'
+ # * <tt>:type</tt> - Override the auto-generated mime type, defaults to 'image/vnd.microsoft.icon'
#
- # The helper accepts an additional options hash where you can override "rel" and "type".
+ # favicon_link_tag '/myicon.ico'
+ # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
- # For example, Mobile Safari looks for a different LINK tag, pointing to an image that
+ # 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 iPod Touch, iPhone, or iPad.
# The following call would generate such a tag:
#
- # <%= favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png' %>
+ # 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" />
+ #
def favicon_link_tag(source='favicon.ico', options={})
tag('link', {
:rel => 'shortcut icon',
@@ -166,7 +165,7 @@ module ActionView
}.merge(options.symbolize_keys))
end
- # Returns an html image tag for the +source+. The +source+ can be a full
+ # Returns an HTML image tag for the +source+. The +source+ can be a full
# path or a file.
#
# ==== Options
@@ -179,18 +178,18 @@ module ActionView
# width="30" and height="45", and "50" becomes width="50" and height="50".
# <tt>:size</tt> will be ignored if the value is not in the correct format.
#
- # image_tag("icon")
- # # => <img alt="Icon" src="/assets/icon" />
- # image_tag("icon.png")
- # # => <img alt="Icon" src="/assets/icon.png" />
- # image_tag("icon.png", size: "16x10", alt: "Edit Entry")
- # # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
- # image_tag("/icons/icon.gif", size: "16")
- # # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
- # image_tag("/icons/icon.gif", height: '32', width: '32')
- # # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
- # image_tag("/icons/icon.gif", class: "menu_icon")
- # # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
+ # image_tag("icon")
+ # # => <img alt="Icon" src="/assets/icon" />
+ # image_tag("icon.png")
+ # # => <img alt="Icon" src="/assets/icon.png" />
+ # image_tag("icon.png", size: "16x10", alt: "Edit Entry")
+ # # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
+ # image_tag("/icons/icon.gif", size: "16")
+ # # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
+ # image_tag("/icons/icon.gif", height: '32', width: '32')
+ # # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
+ # image_tag("/icons/icon.gif", class: "menu_icon")
+ # # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
def image_tag(source, options={})
options = options.symbolize_keys
@@ -208,6 +207,9 @@ module ActionView
tag("img", options)
end
+ # Returns a string suitable for an html image tag alt attribute.
+ # +src+ is meant to be an image file path.
+ # It removes the basename of the file path and the digest, if any.
def image_alt(src)
File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize
end
@@ -228,24 +230,24 @@ module ActionView
# width="30" and height="45". <tt>:size</tt> will be ignored if the
# value is not in the correct format.
#
- # video_tag("trailer")
- # # => <video src="/videos/trailer" />
- # video_tag("trailer.ogg")
- # # => <video src="/videos/trailer.ogg" />
- # video_tag("trailer.ogg", controls: true, autobuffer: true)
- # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
- # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
- # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
- # video_tag("/trailers/hd.avi", size: "16x16")
- # # => <video src="/trailers/hd.avi" width="16" height="16" />
- # video_tag("/trailers/hd.avi", height: '32', width: '32')
- # # => <video height="32" src="/trailers/hd.avi" width="32" />
- # video_tag("trailer.ogg", "trailer.flv")
- # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></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"], size: "160x120")
- # # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
+ # video_tag("trailer")
+ # # => <video src="/videos/trailer" />
+ # video_tag("trailer.ogg")
+ # # => <video src="/videos/trailer.ogg" />
+ # video_tag("trailer.ogg", controls: true, autobuffer: true)
+ # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
+ # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
+ # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
+ # video_tag("/trailers/hd.avi", size: "16x16")
+ # # => <video src="/trailers/hd.avi" width="16" height="16" />
+ # video_tag("/trailers/hd.avi", height: '32', width: '32')
+ # # => <video height="32" src="/trailers/hd.avi" width="32" />
+ # video_tag("trailer.ogg", "trailer.flv")
+ # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></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"], size: "160x120")
+ # # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
def video_tag(*sources)
multiple_sources_tag('video', sources) do |options|
options[:poster] = path_to_image(options[:poster]) if options[:poster]
@@ -256,18 +258,18 @@ module ActionView
end
end
- # Returns an html audio tag for the +source+.
+ # Returns an HTML audio tag for the +source+.
# The +source+ can be full path or file that exists in
# your public audios directory.
#
- # audio_tag("sound") # =>
- # <audio src="/audios/sound" />
- # audio_tag("sound.wav") # =>
- # <audio src="/audios/sound.wav" />
- # audio_tag("sound.wav", autoplay: true, controls: true) # =>
- # <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
- # audio_tag("sound.wav", "sound.mid") # =>
- # <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
+ # audio_tag("sound")
+ # # => <audio src="/audios/sound" />
+ # audio_tag("sound.wav")
+ # # => <audio src="/audios/sound.wav" />
+ # audio_tag("sound.wav", autoplay: true, controls: true)
+ # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
+ # audio_tag("sound.wav", "sound.mid")
+ # # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
def audio_tag(*sources)
multiple_sources_tag('audio', sources)
end
diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb
index 0bb5e739bb..0affac41e8 100644
--- a/actionpack/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb
@@ -2,7 +2,7 @@ require 'zlib'
module ActionView
# = Action View Asset URL Helpers
- module Helpers #:nodoc:
+ module Helpers
# This module provides methods for generating asset paths and
# urls.
#
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index f5ac455208..42b1dd8933 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -2,7 +2,7 @@ require 'set'
module ActionView
# = Action View Atom Feed Helpers
- module Helpers #:nodoc:
+ module Helpers
module AtomFeedHelper
# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
# template languages).
@@ -124,7 +124,7 @@ module ActionView
end
end
- class AtomBuilder
+ class AtomBuilder #:nodoc:
XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
def initialize(xml)
@@ -158,7 +158,7 @@ module ActionView
end
end
- class AtomFeedBuilder < AtomBuilder
+ class AtomFeedBuilder < AtomBuilder #:nodoc:
def initialize(xml, view, feed_options = {})
@xml, @view, @feed_options = xml, view, feed_options
end
diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb
index dfdd5a786d..87fbf8f1a8 100644
--- a/actionpack/lib/action_view/helpers/benchmark_helper.rb
+++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb
@@ -2,7 +2,7 @@ require 'active_support/benchmarkable'
module ActionView
module Helpers
- module BenchmarkHelper
+ module BenchmarkHelper #:nodoc:
include ActiveSupport::Benchmarkable
def benchmark(*)
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index db920ae7a4..8693f4f0e4 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -110,8 +110,15 @@ module ActionView
# <%= some_helper_method(person) %>
#
# Now all you'll have to do is change that timestamp when the helper method changes.
+ #
+ # ==== Conditional caching
+ #
+ # You can pass :if and :unless options, to conditionally perform or skip the cache.
+ #
+ # <%= cache @model, if: some_condition(@model) do %>
+ #
def cache(name = {}, options = nil, &block)
- if controller.perform_caching
+ if controller.perform_caching && conditions_match?(options)
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
else
yield
@@ -136,6 +143,11 @@ module ActionView
end
private
+
+ def conditions_match?(options)
+ !(options && (!options.fetch(:if, true) || options.fetch(:unless, false)))
+ end
+
def fragment_name_with_digest(name) #:nodoc:
if @virtual_path
[
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 85e398e559..4ec860d69a 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -42,14 +42,12 @@ module ActionView
end
# Calling content_for stores a block of markup in an identifier for later use.
- # You can make subsequent calls to the stored content in other templates, helper modules
- # or the layout by passing the identifier as an argument to <tt>content_for</tt>.
+ # In order to access this stored content in other templates, helper modules
+ # or the layout, you would pass the identifier as an argument to <tt>content_for</tt>.
#
# Note: <tt>yield</tt> can still be used to retrieve the stored content, but calling
# <tt>yield</tt> doesn't work in helper modules, while <tt>content_for</tt> does.
#
- # ==== Examples
- #
# <% content_for :not_authorized do %>
# alert('You are not authorized to do that!')
# <% end %>
@@ -74,7 +72,8 @@ module ActionView
#
# <%= stored_content %>
#
- # You can use the <tt>yield</tt> syntax alongside an existing call to <tt>yield</tt> in a layout. For example:
+ # You can also use the <tt>yield</tt> syntax alongside an existing call to
+ # <tt>yield</tt> in a layout. For example:
#
# <%# This is the layout %>
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
@@ -115,7 +114,7 @@ module ActionView
# <li><%= link_to 'Home', action: 'index' %></li>
# <% end %>
#
- # <%# Add some other content, or use a different template: %>
+ # And in other place:
#
# <% content_for :navigation do %>
# <li><%= link_to 'Login', action: 'login' %></li>
@@ -145,8 +144,7 @@ module ActionView
#
# <% content_for :script, javascript_include_tag(:defaults) %>
#
- # WARNING: content_for is ignored in caches. So you shouldn't use it
- # for elements that will be fragment cached.
+ # WARNING: content_for is ignored in caches. So you shouldn't use it for elements that will be fragment cached.
def content_for(name, content = nil, options = {}, &block)
if content || block_given?
if block_given?
@@ -173,13 +171,9 @@ module ActionView
result unless content
end
- # content_for? simply checks whether any content has been captured yet using content_for
+ # content_for? checks whether any content has been captured yet using `content_for`.
# Useful to render parts of your layout differently based on what is in your views.
#
- # ==== Examples
- #
- # Perhaps you will use different css in you layout if no content_for :right_column
- #
# <%# This is the layout %>
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
# <head>
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 6e51ba66a5..1fbf61a5a9 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -45,7 +45,6 @@ module ActionView
# 40-59 secs # => less than a minute
# 60-89 secs # => 1 minute
#
- # ==== Examples
# from_time = Time.now
# distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
# distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
@@ -166,7 +165,6 @@ module ActionView
# Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
# attribute (identified by +method+) on an object assigned to the template (identified by +object+).
#
- #
# ==== Options
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
# "2" instead of "February").
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index c79d30ea88..17386a57b8 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -460,8 +460,6 @@ module ActionView
# doesn't create the form tags themselves. This makes fields_for suitable
# for specifying additional model objects in the same form.
#
- # === Generic Examples
- #
# Although the usage and purpose of +field_for+ is similar to +form_for+'s,
# its method signature is slightly different. Like +form_for+, it yields
# a FormBuilder object associated with a particular model object to a block,
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 9310a90f0f..c0e7ee1f8d 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -7,13 +7,11 @@ module ActionView
# = Action View Form Option Helpers
module Helpers
# Provides a number of methods for turning different kinds of containers into a set of option tags.
- # == Options
+ #
# The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
#
# * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
#
- # For example,
- #
# select("post", "category", Post::CATEGORIES, {include_blank: true})
#
# could become:
@@ -24,7 +22,7 @@ module ActionView
# <option>poem</option>
# </select>
#
- # Another common case is a select tag for an <tt>belongs_to</tt>-associated object.
+ # Another common case is a select tag for a <tt>belongs_to</tt>-associated object.
#
# Example with @post.person_id => 2:
#
@@ -41,8 +39,6 @@ module ActionView
#
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
#
- # Example:
- #
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
#
# could become:
@@ -57,8 +53,6 @@ module ActionView
# Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
# option to be in the +html_options+ parameter.
#
- # Example:
- #
# select("album[]", "genre", %w[rap rock country], {}, { index: nil })
#
# becomes:
@@ -71,8 +65,6 @@ module ActionView
#
# * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
#
- # Example:
- #
# select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
#
# could become:
@@ -86,8 +78,6 @@ module ActionView
#
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
#
- # Example:
- #
# collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
#
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
@@ -152,7 +142,8 @@ module ActionView
# form, and parameters extraction gets the last occurrence of any repeated
# key in the query string, that works for ordinary forms.
#
- # In case if you don't want the helper to generate this hidden field you can specify <tt>include_hidden: false</tt> option.
+ # In case if you don't want the helper to generate this hidden field you can specify
+ # <tt>include_hidden: false</tt> option.
#
def select(object, method, choices, options = {}, html_options = {})
Tags::Select.new(object, method, self, choices, options, html_options).render
@@ -170,9 +161,11 @@ module ActionView
# retrieve the value/text.
#
# Example object structure for use with this method:
+ #
# class Post < ActiveRecord::Base
# belongs_to :author
# end
+ #
# class Author < ActiveRecord::Base
# has_many :posts
# def name_with_initial
@@ -181,6 +174,7 @@ module ActionView
# end
#
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
+ #
# collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
#
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
@@ -213,23 +207,28 @@ module ActionView
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
#
# Example object structure for use with this method:
+ #
# class Continent < ActiveRecord::Base
# has_many :countries
# # attribs: id, name
# end
+ #
# class Country < ActiveRecord::Base
# belongs_to :continent
# # attribs: id, name, continent_id
# end
+ #
# class City < ActiveRecord::Base
# belongs_to :country
# # attribs: id, name, country_id
# end
#
# Sample usage:
+ #
# grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
#
# Possible output:
+ #
# <select name="city[country_id]">
# <optgroup label="Africa">
# <option value="1">South Africa</option>
@@ -284,57 +283,54 @@ module ActionView
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
# may also be an array of values to be selected when using a multiple select.
#
- # Examples (call, result):
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
- # # <option value="$">Dollar</option>
- # # <option value="DKK">Kroner</option>
+ # # => <option value="$">Dollar</option>
+ # # => <option value="DKK">Kroner</option>
#
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
- # # <option>VISA</option>
- # # <option selected="selected">MasterCard</option>
+ # # => <option>VISA</option>
+ # # => <option selected="selected">MasterCard</option>
#
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
- # # <option value="$20">Basic</option>
- # # <option value="$40" selected="selected">Plus</option>
+ # # => <option value="$20">Basic</option>
+ # # => <option value="$40" selected="selected">Plus</option>
#
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
- # # <option selected="selected">VISA</option>
- # # <option>MasterCard</option>
- # # <option selected="selected">Discover</option>
+ # # => <option selected="selected">VISA</option>
+ # # => <option>MasterCard</option>
+ # # => <option selected="selected">Discover</option>
#
# You can optionally provide html attributes as the last element of the array.
#
- # Examples:
# options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
- # # <option value="Denmark">Denmark</option>
- # # <option value="USA" class="bold" selected="selected">USA</option>
- # # <option value="Sweden" selected="selected">Sweden</option>
+ # # => <option value="Denmark">Denmark</option>
+ # # => <option value="USA" class="bold" selected="selected">USA</option>
+ # # => <option value="Sweden" selected="selected">Sweden</option>
#
# options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
- # # <option value="$" class="bold">Dollar</option>
- # # <option value="DKK" onclick="alert('HI');">Kroner</option>
+ # # => <option value="$" class="bold">Dollar</option>
+ # # => <option value="DKK" onclick="alert('HI');">Kroner</option>
#
# If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
# or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
#
- # Examples:
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: "Super Platinum")
- # # <option value="Free">Free</option>
- # # <option value="Basic">Basic</option>
- # # <option value="Advanced">Advanced</option>
- # # <option value="Super Platinum" disabled="disabled">Super Platinum</option>
+ # # => <option value="Free">Free</option>
+ # # => <option value="Basic">Basic</option>
+ # # => <option value="Advanced">Advanced</option>
+ # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
#
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: ["Advanced", "Super Platinum"])
- # # <option value="Free">Free</option>
- # # <option value="Basic">Basic</option>
- # # <option value="Advanced" disabled="disabled">Advanced</option>
- # # <option value="Super Platinum" disabled="disabled">Super Platinum</option>
+ # # => <option value="Free">Free</option>
+ # # => <option value="Basic">Basic</option>
+ # # => <option value="Advanced" disabled="disabled">Advanced</option>
+ # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
#
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], selected: "Free", disabled: "Super Platinum")
- # # <option value="Free" selected="selected">Free</option>
- # # <option value="Basic">Basic</option>
- # # <option value="Advanced">Advanced</option>
- # # <option value="Super Platinum" disabled="disabled">Super Platinum</option>
+ # # => <option value="Free" selected="selected">Free</option>
+ # # => <option value="Basic">Basic</option>
+ # # => <option value="Advanced">Advanced</option>
+ # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def options_for_select(container, selected = nil)
@@ -358,12 +354,12 @@ module ActionView
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
- # Example:
+ #
# options_from_collection_for_select(@people, 'id', 'name')
- # This will output the same HTML as if you did this:
- # <option value="#{person.id}">#{person.name}</option>
+ # # => <option value="#{person.id}">#{person.name}</option>
#
# This is more often than not used inside a #select_tag like this example:
+ #
# select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
#
# If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+
@@ -412,10 +408,12 @@ module ActionView
# to be specified.
#
# Example object structure for use with this method:
+ #
# class Continent < ActiveRecord::Base
# has_many :countries
# # attribs: id, name
# end
+ #
# class Country < ActiveRecord::Base
# belongs_to :continent
# # attribs: id, name, continent_id
@@ -465,7 +463,6 @@ module ActionView
# prepends an option with a generic prompt - "Please select" - or the given prompt string.
# * <tt>:divider</tt> - the divider for the options groups.
#
- # Sample usage (Array):
# grouped_options = [
# ['North America',
# [['United States','US'],'Canada']],
@@ -474,7 +471,6 @@ module ActionView
# ]
# grouped_options_for_select(grouped_options)
#
- # Sample usage (Hash):
# grouped_options = {
# 'North America' => [['United States','US'], 'Canada'],
# 'Europe' => ['Denmark','Germany','France']
@@ -492,7 +488,6 @@ module ActionView
# <option value="France">France</option>
# </optgroup>
#
- # Sample usage (divider):
# grouped_options = [
# [['United States','US'], 'Canada'],
# ['Denmark','Germany','France']
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 1a99fc7091..cfdd7c77d8 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -18,7 +18,8 @@ module ActionView
# Escapes carriage returns and single and double quotes for JavaScript segments.
#
- # Also available through the alias j(). This is particularly helpful in JavaScript responses, like:
+ # Also available through the alias j(). This is particularly helpful in JavaScript
+ # responses, like:
#
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
def escape_javascript(javascript)
@@ -43,12 +44,14 @@ module ActionView
# </script>
#
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
- # tag. Example:
+ # tag.
+ #
# javascript_tag "alert('All is good')", defer: 'defer'
# # => <script defer="defer">alert('All is good')</script>
#
# Instead of passing the content as an argument, you can also use a block
# in which case, you pass your +html_options+ as the first parameter.
+ #
# <%= javascript_tag defer: 'defer' do -%>
# alert('All is good')
# <% end -%>
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 82340171af..9e1be65b1a 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -28,8 +28,6 @@ module ActionView
# Formats a +number+ into a US phone number (e.g., (555)
# 123-9876). You can customize the format in the +options+ hash.
#
- # ==== Options
- #
# * <tt>:area_code</tt> - Adds parentheses around the area code.
# * <tt>:delimiter</tt> - Specifies the delimiter to use
# (defaults to "-").
@@ -40,21 +38,18 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
- # ==== Examples
- #
- # number_to_phone(5551234) # => 555-1234
- # number_to_phone("5551234") # => 555-1234
- # number_to_phone(1235551234) # => 123-555-1234
- # number_to_phone(1235551234, area_code: true) # => (123) 555-1234
- # number_to_phone(1235551234, delimiter: " ") # => 123 555 1234
- # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
- # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
- # number_to_phone("123a456") # => 123a456
- #
- # number_to_phone("1234a567", raise: true) # => InvalidNumberError
- #
- # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
- # # => +1.123.555.1234 x 1343
+ # number_to_phone(5551234) # => 555-1234
+ # number_to_phone("5551234") # => 555-1234
+ # number_to_phone(1235551234) # => 123-555-1234
+ # number_to_phone(1235551234, area_code: true) # => (123) 555-1234
+ # number_to_phone(1235551234, delimiter: " ") # => 123 555 1234
+ # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
+ # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
+ # number_to_phone("123a456") # => 123a456
+ # number_to_phone("1234a567", raise: true) # => InvalidNumberError
+ #
+ # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
+ # # => +1.123.555.1234 x 1343
def number_to_phone(number, options = {})
return unless number
options = options.symbolize_keys
@@ -66,8 +61,6 @@ module ActionView
# Formats a +number+ into a currency string (e.g., $13.65). You
# can customize the format in the +options+ hash.
#
- # ==== Options
- #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the level of precision (defaults
@@ -89,22 +82,20 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
- # ==== Examples
- #
- # number_to_currency(1234567890.50) # => $1,234,567,890.50
- # number_to_currency(1234567890.506) # => $1,234,567,890.51
- # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
- # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
- # number_to_currency("123a456") # => $123a456
+ # number_to_currency(1234567890.50) # => $1,234,567,890.50
+ # number_to_currency(1234567890.506) # => $1,234,567,890.51
+ # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
+ # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
+ # number_to_currency("123a456") # => $123a456
#
- # number_to_currency("123a456", raise: true) # => InvalidNumberError
+ # number_to_currency("123a456", raise: true) # => InvalidNumberError
#
- # number_to_currency(-1234567890.50, negative_format: "(%u%n)")
- # # => ($1,234,567,890.50)
- # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "")
- # # => &pound;1234567890,50
- # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "", format: "%n %u")
- # # => 1234567890,50 &pound;
+ # number_to_currency(-1234567890.50, negative_format: "(%u%n)")
+ # # => ($1,234,567,890.50)
+ # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "")
+ # # => &pound;1234567890,50
+ # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "", format: "%n %u")
+ # # => 1234567890,50 &pound;
def number_to_currency(number, options = {})
return unless number
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
@@ -117,7 +108,6 @@ module ActionView
# Formats a +number+ as a percentage string (e.g., 65%). You can
# customize the format in the +options+ hash.
#
- # ==== Options
#
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
@@ -138,18 +128,16 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
- # ==== Examples
+ # number_to_percentage(100) # => 100.000%
+ # number_to_percentage("98") # => 98.000%
+ # number_to_percentage(100, precision: 0) # => 100%
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
+ # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
+ # number_to_percentage(1000, locale: :fr) # => 1 000,000%
+ # number_to_percentage("98a") # => 98a%
+ # number_to_percentage(100, format: "%n %") # => 100 %
#
- # number_to_percentage(100) # => 100.000%
- # number_to_percentage("98") # => 98.000%
- # number_to_percentage(100, precision: 0) # => 100%
- # number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
- # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
- # number_to_percentage(1000, locale: :fr) # => 1 000,000%
- # number_to_percentage("98a") # => 98a%
- # number_to_percentage(100, format: "%n %") # => 100 %
- #
- # number_to_percentage("98a", raise: true) # => InvalidNumberError
+ # number_to_percentage("98a", raise: true) # => InvalidNumberError
def number_to_percentage(number, options = {})
return unless number
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
@@ -163,8 +151,6 @@ module ActionView
# (e.g., 12,324). You can customize the format in the +options+
# hash.
#
- # ==== Options
- #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
@@ -174,20 +160,18 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
- # ==== Examples
- #
- # number_with_delimiter(12345678) # => 12,345,678
- # number_with_delimiter("123456") # => 123,456
- # number_with_delimiter(12345678.05) # => 12,345,678.05
- # number_with_delimiter(12345678, delimiter: ".") # => 12.345.678
- # number_with_delimiter(12345678, delimiter: ",") # => 12,345,678
- # number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05
- # number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05
- # number_with_delimiter("112a") # => 112a
- # number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
- # # => 98 765 432,98
- #
- # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
+ # number_with_delimiter(12345678) # => 12,345,678
+ # number_with_delimiter("123456") # => 123,456
+ # number_with_delimiter(12345678.05) # => 12,345,678.05
+ # number_with_delimiter(12345678, delimiter: ".") # => 12.345.678
+ # number_with_delimiter(12345678, delimiter: ",") # => 12,345,678
+ # number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05
+ # number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05
+ # number_with_delimiter("112a") # => 112a
+ # number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
+ # # => 98 765 432,98
+ #
+ # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
def number_with_delimiter(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
@@ -201,8 +185,6 @@ module ActionView
# +:significant+ is +false+, and 5 if +:significant+ is +true+).
# You can customize the format in the +options+ hash.
#
- # ==== Options
- #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
@@ -220,23 +202,21 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
- # ==== Examples
- #
- # number_with_precision(111.2345) # => 111.235
- # number_with_precision(111.2345, precision: 2) # => 111.23
- # number_with_precision(13, precision: 5) # => 13.00000
- # number_with_precision(389.32314, precision: 0) # => 389
- # number_with_precision(111.2345, significant: true) # => 111
- # number_with_precision(111.2345, precision: 1, significant: true) # => 100
- # number_with_precision(13, precision: 5, significant: true) # => 13.000
- # number_with_precision(111.234, locale: :fr) # => 111,234
- #
- # number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
- # # => 13
- #
- # number_with_precision(389.32314, precision: 4, significant: true) # => 389.3
- # number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
- # # => 1.111,23
+ # number_with_precision(111.2345) # => 111.235
+ # number_with_precision(111.2345, precision: 2) # => 111.23
+ # number_with_precision(13, precision: 5) # => 13.00000
+ # number_with_precision(389.32314, precision: 0) # => 389
+ # number_with_precision(111.2345, significant: true) # => 111
+ # number_with_precision(111.2345, precision: 1, significant: true) # => 100
+ # number_with_precision(13, precision: 5, significant: true) # => 13.000
+ # number_with_precision(111.234, locale: :fr) # => 111,234
+ #
+ # number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
+ # # => 13
+ #
+ # number_with_precision(389.32314, precision: 4, significant: true) # => 389.3
+ # number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
+ # # => 1.111,23
def number_with_precision(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
@@ -245,7 +225,6 @@ module ActionView
}
end
-
# Formats the bytes in +number+ into a more understandable
# representation (e.g., giving it 1500 yields 1.5 KB). This
# method is useful for reporting file sizes to users. You can
@@ -254,8 +233,6 @@ module ActionView
# See <tt>number_to_human</tt> if you want to pretty-print a
# generic number.
#
- # ==== Options
- #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
@@ -275,24 +252,23 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
- # ==== Examples
- #
- # number_to_human_size(123) # => 123 Bytes
- # number_to_human_size(1234) # => 1.21 KB
- # number_to_human_size(12345) # => 12.1 KB
- # number_to_human_size(1234567) # => 1.18 MB
- # number_to_human_size(1234567890) # => 1.15 GB
- # number_to_human_size(1234567890123) # => 1.12 TB
- # number_to_human_size(1234567, precision: 2) # => 1.2 MB
- # number_to_human_size(483989, precision: 2) # => 470 KB
- # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
+ # number_to_human_size(123) # => 123 Bytes
+ # number_to_human_size(1234) # => 1.21 KB
+ # number_to_human_size(12345) # => 12.1 KB
+ # number_to_human_size(1234567) # => 1.18 MB
+ # number_to_human_size(1234567890) # => 1.15 GB
+ # number_to_human_size(1234567890123) # => 1.12 TB
+ # number_to_human_size(1234567, precision: 2) # => 1.2 MB
+ # number_to_human_size(483989, precision: 2) # => 470 KB
+ # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
#
# Non-significant zeros after the fractional separator are
# stripped out by default (set
# <tt>:strip_insignificant_zeros</tt> to +false+ to change
# that):
- # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
- # number_to_human_size(524288000, precision: 5) # => "500 MB"
+ #
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
def number_to_human_size(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
@@ -348,29 +324,27 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
- # ==== Examples
- #
- # number_to_human(123) # => "123"
- # number_to_human(1234) # => "1.23 Thousand"
- # number_to_human(12345) # => "12.3 Thousand"
- # number_to_human(1234567) # => "1.23 Million"
- # number_to_human(1234567890) # => "1.23 Billion"
- # number_to_human(1234567890123) # => "1.23 Trillion"
- # number_to_human(1234567890123456) # => "1.23 Quadrillion"
- # number_to_human(1234567890123456789) # => "1230 Quadrillion"
- # number_to_human(489939, precision: 2) # => "490 Thousand"
- # number_to_human(489939, precision: 4) # => "489.9 Thousand"
- # number_to_human(1234567, precision: 4,
- # significant: false) # => "1.2346 Million"
- # number_to_human(1234567, precision: 1,
+ # number_to_human(123) # => "123"
+ # number_to_human(1234) # => "1.23 Thousand"
+ # number_to_human(12345) # => "12.3 Thousand"
+ # number_to_human(1234567) # => "1.23 Million"
+ # number_to_human(1234567890) # => "1.23 Billion"
+ # number_to_human(1234567890123) # => "1.23 Trillion"
+ # number_to_human(1234567890123456) # => "1.23 Quadrillion"
+ # number_to_human(1234567890123456789) # => "1230 Quadrillion"
+ # number_to_human(489939, precision: 2) # => "490 Thousand"
+ # number_to_human(489939, precision: 4) # => "489.9 Thousand"
+ # number_to_human(1234567, precision: 4,
+ # significant: false) # => "1.2346 Million"
+ # number_to_human(1234567, precision: 1,
# separator: ',',
- # significant: false) # => "1,2 Million"
+ # significant: false) # => "1,2 Million"
#
# Non-significant zeros after the decimal separator are stripped
# out by default (set <tt>:strip_insignificant_zeros</tt> to
# +false+ to change that):
- # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
- # number_to_human(500000000, precision: 5) # => "500 Million"
+ # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
+ # number_to_human(500000000, precision: 5) # => "500 Million"
#
# ==== Custom Unit Quantifiers
#
@@ -392,12 +366,12 @@ module ActionView
#
# Then you could do:
#
- # number_to_human(543934, units: :distance) # => "544 kilometers"
- # number_to_human(54393498, units: :distance) # => "54400 kilometers"
- # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
- # number_to_human(343, units: :distance, precision: 1) # => "300 meters"
- # number_to_human(1, units: :distance) # => "1 meter"
- # number_to_human(0.34, units: :distance) # => "34 centimeters"
+ # number_to_human(543934, units: :distance) # => "544 kilometers"
+ # number_to_human(54393498, units: :distance) # => "54400 kilometers"
+ # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
+ # number_to_human(343, units: :distance, precision: 1) # => "300 meters"
+ # number_to_human(1, units: :distance) # => "1 meter"
+ # number_to_human(0.34, units: :distance) # => "34 centimeters"
#
def number_to_human(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
diff --git a/actionpack/lib/action_view/helpers/output_safety_helper.rb b/actionpack/lib/action_view/helpers/output_safety_helper.rb
index 2e7e9dc50c..60a4478c26 100644
--- a/actionpack/lib/action_view/helpers/output_safety_helper.rb
+++ b/actionpack/lib/action_view/helpers/output_safety_helper.rb
@@ -11,7 +11,8 @@ module ActionView #:nodoc:
#
# For example:
#
- # <%=raw @user.name %>
+ # raw @user.name
+ # # => 'Jimmy <alert>Tables</alert>'
def raw(stringish)
stringish.to_s.html_safe
end
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index e6f61d269c..e5cb843670 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -3,7 +3,7 @@ require 'action_view/vendor/html-scanner'
module ActionView
# = Action View Sanitize Helpers
- module Helpers #:nodoc:
+ module Helpers
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
# These helper methods extend Action View making them callable within your template files.
module SanitizeHelper
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 3875d88a9f..e80e0ed9b0 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -3,7 +3,7 @@ require "rails"
module ActionView
# = Action View Railtie
- class Railtie < Rails::Railtie
+ class Railtie < Rails::Railtie # :nodoc:
config.action_view = ActiveSupport::OrderedOptions.new
config.action_view.embed_authenticity_token_in_remote_forms = false
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index f5fdf766ad..8fb9b6ff18 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -293,7 +293,7 @@ module ActionView
object, as = @object, @variable
if !block && (layout = @options[:layout])
- layout = find_template(layout, @template_keys)
+ layout = find_template(layout.to_s, @template_keys)
end
object ||= locals[as]
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 731d8f9dab..afbbece90f 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -14,6 +14,17 @@ module ActionView
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
end
+ # Erubis toggles <%= and <%== behavior when escaping is enabled.
+ # We override to always treat <%== as escaped.
+ def add_expr(src, code, indicator)
+ case indicator
+ when '=='
+ add_expr_escaped(src, code)
+ else
+ super
+ end
+ end
+
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
def add_expr_literal(src, code)
diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb
index 5d1a703c55..1090af3060 100644
--- a/actionpack/test/abstract/callbacks_test.rb
+++ b/actionpack/test/abstract/callbacks_test.rb
@@ -28,9 +28,9 @@ module AbstractController
end
class Callback2 < ControllerWithCallbacks
- before_filter :first
- after_filter :second
- around_filter :aroundz
+ before_action :first
+ after_action :second
+ around_action :aroundz
def first
@text = "Hello world"
@@ -53,7 +53,7 @@ module AbstractController
end
class Callback2Overwrite < Callback2
- before_filter :first, :except => :index
+ before_action :first, except: :index
end
class TestCallbacks2 < ActiveSupport::TestCase
@@ -61,22 +61,22 @@ module AbstractController
@controller = Callback2.new
end
- test "before_filter works" do
+ test "before_action works" do
@controller.process(:index)
assert_equal "Hello world", @controller.response_body
end
- test "after_filter works" do
+ test "after_action works" do
@controller.process(:index)
assert_equal "Goodbye", @controller.instance_variable_get("@second")
end
- test "around_filter works" do
+ test "around_action works" do
@controller.process(:index)
assert_equal "FIRSTSECOND", @controller.instance_variable_get("@aroundz")
end
- test "before_filter with overwritten condition" do
+ test "before_action with overwritten condition" do
@controller = Callback2Overwrite.new
@controller.process(:index)
assert_equal "", @controller.response_body
@@ -84,11 +84,11 @@ module AbstractController
end
class Callback3 < ControllerWithCallbacks
- before_filter do |c|
+ before_action do |c|
c.instance_variable_set("@text", "Hello world")
end
- after_filter do |c|
+ after_action do |c|
c.instance_variable_set("@second", "Goodbye")
end
@@ -102,20 +102,20 @@ module AbstractController
@controller = Callback3.new
end
- test "before_filter works with procs" do
+ test "before_action works with procs" do
@controller.process(:index)
assert_equal "Hello world", @controller.response_body
end
- test "after_filter works with procs" do
+ test "after_action works with procs" do
@controller.process(:index)
assert_equal "Goodbye", @controller.instance_variable_get("@second")
end
end
class CallbacksWithConditions < ControllerWithCallbacks
- before_filter :list, :only => :index
- before_filter :authenticate, :except => :index
+ before_action :list, :only => :index
+ before_action :authenticate, :except => :index
def index
self.response_body = @list.join(", ")
@@ -141,25 +141,25 @@ module AbstractController
@controller = CallbacksWithConditions.new
end
- test "when :only is specified, a before filter is triggered on that action" do
+ test "when :only is specified, a before action is triggered on that action" do
@controller.process(:index)
assert_equal "Hello, World", @controller.response_body
end
- test "when :only is specified, a before filter is not triggered on other actions" do
+ test "when :only is specified, a before action is not triggered on other actions" do
@controller.process(:sekrit_data)
assert_equal "true", @controller.response_body
end
- test "when :except is specified, an after filter is not triggered on that action" do
+ test "when :except is specified, an after action is not triggered on that action" do
@controller.process(:index)
assert !@controller.instance_variable_defined?("@authenticated")
end
end
class CallbacksWithArrayConditions < ControllerWithCallbacks
- before_filter :list, :only => [:index, :listy]
- before_filter :authenticate, :except => [:index, :listy]
+ before_action :list, only: [:index, :listy]
+ before_action :authenticate, except: [:index, :listy]
def index
self.response_body = @list.join(", ")
@@ -185,24 +185,24 @@ module AbstractController
@controller = CallbacksWithArrayConditions.new
end
- test "when :only is specified with an array, a before filter is triggered on that action" do
+ test "when :only is specified with an array, a before action is triggered on that action" do
@controller.process(:index)
assert_equal "Hello, World", @controller.response_body
end
- test "when :only is specified with an array, a before filter is not triggered on other actions" do
+ test "when :only is specified with an array, a before action is not triggered on other actions" do
@controller.process(:sekrit_data)
assert_equal "true", @controller.response_body
end
- test "when :except is specified with an array, an after filter is not triggered on that action" do
+ test "when :except is specified with an array, an after action is not triggered on that action" do
@controller.process(:index)
assert !@controller.instance_variable_defined?("@authenticated")
end
end
class ChangedConditions < Callback2
- before_filter :first, :only => :index
+ before_action :first, :only => :index
def not_index
@text ||= nil
@@ -227,7 +227,7 @@ module AbstractController
end
class SetsResponseBody < ControllerWithCallbacks
- before_filter :set_body
+ before_action :set_body
def index
self.response_body = "Fail"
@@ -266,6 +266,50 @@ module AbstractController
end
end
+ class AliasedCallbacks < ControllerWithCallbacks
+ before_filter :first
+ after_filter :second
+ around_filter :aroundz
+
+ def first
+ @text = "Hello world"
+ end
+
+ def second
+ @second = "Goodbye"
+ end
+
+ def aroundz
+ @aroundz = "FIRST"
+ yield
+ @aroundz << "SECOND"
+ end
+ def index
+ @text ||= nil
+ self.response_body = @text.to_s
+ end
+ end
+
+ class TestAliasedCallbacks < ActiveSupport::TestCase
+ def setup
+ @controller = AliasedCallbacks.new
+ end
+
+ test "before_filter works" do
+ @controller.process(:index)
+ assert_equal "Hello world", @controller.response_body
+ end
+
+ test "after_filter works" do
+ @controller.process(:index)
+ assert_equal "Goodbye", @controller.instance_variable_get("@second")
+ end
+
+ test "around_filter works" do
+ @controller.process(:index)
+ assert_equal "FIRSTSECOND", @controller.instance_variable_get("@aroundz")
+ end
+ end
end
end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 6414ba3994..9d4356f546 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -53,8 +53,8 @@ class FlashTest < ActionController::TestCase
render :inline => "hello"
end
- # methods for test_sweep_after_halted_filter_chain
- before_filter :halt_and_redir, :only => "filter_halting_action"
+ # methods for test_sweep_after_halted_action_chain
+ before_action :halt_and_redir, only: 'filter_halting_action'
def std_action
@flash_copy = {}.update(flash)
@@ -159,7 +159,7 @@ class FlashTest < ActionController::TestCase
assert_nil session["flash"]
end
- def test_sweep_after_halted_filter_chain
+ def test_sweep_after_halted_action_chain
get :std_action
assert_nil assigns["flash_copy"]["foo"]
get :filter_halting_action
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb
index 2dcfda02a7..90548d4294 100644
--- a/actionpack/test/controller/http_basic_authentication_test.rb
+++ b/actionpack/test/controller/http_basic_authentication_test.rb
@@ -2,9 +2,9 @@ require 'abstract_unit'
class HttpBasicAuthenticationTest < ActionController::TestCase
class DummyController < ActionController::Base
- before_filter :authenticate, :only => :index
- before_filter :authenticate_with_request, :only => :display
- before_filter :authenticate_long_credentials, :only => :show
+ before_action :authenticate, only: :index
+ before_action :authenticate_with_request, only: :display
+ before_action :authenticate_long_credentials, only: :show
http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index c4a94264c3..537de7a2dd 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -4,8 +4,8 @@ require 'active_support/key_generator'
class HttpDigestAuthenticationTest < ActionController::TestCase
class DummyDigestController < ActionController::Base
- before_filter :authenticate, :only => :index
- before_filter :authenticate_with_request, :only => :display
+ before_action :authenticate, only: :index
+ before_action :authenticate_with_request, only: :display
USERS = { 'lifo' => 'world', 'pretty' => 'please',
'dhh' => ::Digest::MD5::hexdigest(["dhh","SuperSecret","secret"].join(":"))}
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index ad4e743be8..8a409d6ed2 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -2,9 +2,9 @@ require 'abstract_unit'
class HttpTokenAuthenticationTest < ActionController::TestCase
class DummyController < ActionController::Base
- before_filter :authenticate, :only => :index
- before_filter :authenticate_with_request, :only => :display
- before_filter :authenticate_long_credentials, :only => :show
+ before_action :authenticate, only: :index
+ before_action :authenticate_with_request, only: :display
+ before_action :authenticate_long_credentials, only: :show
def index
render :text => "Hello Secret"
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index 9efb6ab95f..929545fc10 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -13,7 +13,7 @@ module Another
head :status => 406
end
- before_filter :redirector, :only => :never_executed
+ before_action :redirector, only: :never_executed
def never_executed
end
@@ -26,6 +26,10 @@ module Another
redirect_to "http://foo.bar/"
end
+ def filterable_redirector
+ redirect_to "http://secret.foo.bar/"
+ end
+
def data_sender
send_data "cool data", :filename => "file.txt"
end
@@ -42,6 +46,22 @@ module Another
render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>"
end
+ def with_fragment_cache_and_if_true_condition
+ render :inline => "<%= cache('foo', :if => true) { 'bar' } %>"
+ end
+
+ def with_fragment_cache_and_if_false_condition
+ render :inline => "<%= cache('foo', :if => false) { 'bar' } %>"
+ end
+
+ def with_fragment_cache_and_unless_false_condition
+ render :inline => "<%= cache('foo', :unless => false) { 'bar' } %>"
+ end
+
+ def with_fragment_cache_and_unless_true_condition
+ render :inline => "<%= cache('foo', :unless => true) { 'bar' } %>"
+ end
+
def with_exception
raise Exception
end
@@ -152,6 +172,24 @@ class ACLogSubscriberTest < ActionController::TestCase
assert_equal "Redirected to http://foo.bar/", logs[1]
end
+ def test_filter_redirect_url_by_string
+ @request.env['action_dispatch.redirect_filter'] = ['secret']
+ get :filterable_redirector
+ wait
+
+ assert_equal 3, logs.size
+ assert_equal "Redirected to [FILTERED]", logs[1]
+ end
+
+ def test_filter_redirect_url_by_regexp
+ @request.env['action_dispatch.redirect_filter'] = [/secret\.foo.+/]
+ get :filterable_redirector
+ wait
+
+ assert_equal 3, logs.size
+ assert_equal "Redirected to [FILTERED]", logs[1]
+ end
+
def test_send_data
get :data_sender
wait
@@ -181,6 +219,54 @@ class ACLogSubscriberTest < ActionController::TestCase
@controller.config.perform_caching = true
end
+ def test_with_fragment_cache_and_if_true
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_if_true_condition
+ wait
+
+ assert_equal 4, logs.size
+ assert_match(/Read fragment views\/foo/, logs[1])
+ assert_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
+ def test_with_fragment_cache_and_if_false
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_if_false_condition
+ wait
+
+ assert_equal 2, logs.size
+ assert_no_match(/Read fragment views\/foo/, logs[1])
+ assert_no_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
+ def test_with_fragment_cache_and_unless_true
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_unless_true_condition
+ wait
+
+ assert_equal 2, logs.size
+ assert_no_match(/Read fragment views\/foo/, logs[1])
+ assert_no_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
+ def test_with_fragment_cache_and_unless_false
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_unless_false_condition
+ wait
+
+ assert_equal 4, logs.size
+ assert_match(/Read fragment views\/foo/, logs[1])
+ assert_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
def test_with_fragment_cache_and_percent_in_key
@controller.config.perform_caching = true
get :with_fragment_cache_and_percent_in_key
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index d183b0be17..ed013e2185 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -1139,7 +1139,7 @@ end
# For testing layouts which are set automatically
class PostController < AbstractPostController
- around_filter :with_iphone
+ around_action :with_iphone
def index
respond_to(:html, :iphone, :js)
diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb
index ed244513a5..964f22eb03 100644
--- a/actionpack/test/controller/new_base/base_test.rb
+++ b/actionpack/test/controller/new_base/base_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
# Tests the controller dispatching happy path
module Dispatching
class SimpleController < ActionController::Base
- before_filter :authenticate
+ before_action :authenticate
def index
render :text => "success"
diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb
index f41b14d5d6..177a1c088d 100644
--- a/actionpack/test/controller/new_base/render_context_test.rb
+++ b/actionpack/test/controller/new_base/render_context_test.rb
@@ -14,7 +14,7 @@ module RenderContext
include ActionView::Context
# 2) Call _prepare_context that will do the required initialization
- before_filter :_prepare_context
+ before_action :_prepare_context
def hello_world
@value = "Hello"
diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb
index d0be4f66d1..6b2ae2b2a9 100644
--- a/actionpack/test/controller/new_base/render_template_test.rb
+++ b/actionpack/test/controller/new_base/render_template_test.rb
@@ -9,7 +9,8 @@ module RenderTemplate
"locals.html.erb" => "The secret is <%= secret %>",
"xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend",
"with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>",
- "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %>",
+ "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a html template",
+ "with_implicit_raw.text.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a text template",
"test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>",
"test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>",
"test/final.json.erb" => "{ final: json }",
@@ -113,7 +114,12 @@ module RenderTemplate
get :with_implicit_raw
- assert_body "Hello <strong>this is also raw</strong>"
+ assert_body "Hello <strong>this is also raw</strong> in a html template"
+ assert_status 200
+
+ get :with_implicit_raw, format: 'text'
+
+ assert_body "Hello <strong>this is also raw</strong> in a text template"
assert_status 200
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 859ed1466b..7640bc12a2 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -37,7 +37,7 @@ end
class TestController < ActionController::Base
protect_from_forgery
- before_filter :set_variable_for_layout
+ before_action :set_variable_for_layout
class LabellingFormBuilder < ActionView::Helpers::FormBuilder
end
@@ -137,7 +137,7 @@ class TestController < ActionController::Base
def conditional_hello_with_bangs
render :action => 'hello_world'
end
- before_filter :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs
+ before_action :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs
def handle_last_modified_and_etags
fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ])
@@ -710,7 +710,7 @@ class TestController < ActionController::Base
render :action => "calling_partial_with_layout", :layout => "layouts/partial_with_layout"
end
- before_filter :only => :render_with_filters do
+ before_action only: :render_with_filters do
request.format = :xml
end
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index 48e2d6491e..4898b0c57f 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -68,9 +68,9 @@ class RescueController < ActionController::Base
render :text => 'io error'
end
- before_filter(:only => :before_filter_raises) { raise 'umm nice' }
+ before_action(only: :before_action_raises) { raise 'umm nice' }
- def before_filter_raises
+ def before_action_raises
end
def raises
diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb
index 718d06ef38..888791b874 100644
--- a/actionpack/test/controller/show_exceptions_test.rb
+++ b/actionpack/test/controller/show_exceptions_test.rb
@@ -5,7 +5,7 @@ module ShowExceptions
use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
use ActionDispatch::DebugExceptions
- before_filter :only => :another_boom do
+ before_action only: :another_boom do
request.env["action_dispatch.show_detailed_exceptions"] = true
end
diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb
index 40f6dc6f0f..c6e7a523b9 100644
--- a/actionpack/test/controller/view_paths_test.rb
+++ b/actionpack/test/controller/view_paths_test.rb
@@ -4,7 +4,7 @@ class ViewLoadPathsTest < ActionController::TestCase
class TestController < ActionController::Base
def self.controller_path() "test" end
- before_filter :add_view_path, :only => :hello_world_at_request_time
+ before_action :add_view_path, only: :hello_world_at_request_time
def hello_world() end
def hello_world_at_request_time() render(:action => 'hello_world') end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 4f5d8fdb7c..cb5299e8d3 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -3065,6 +3065,35 @@ class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest
end
end
+class TestGlobRoutingMapper < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ get "/*id" => redirect("/not_cars"), :constraints => {id: /dummy/}
+ get "/cars" => ok
+ end
+ end
+
+ #include Routes.url_helpers
+ def app; Routes end
+
+ def test_glob_constraint
+ get "/dummy"
+ assert_equal "301", @response.code
+ assert_equal "/not_cars", @response.header['Location'].match('/[^/]+$')[0]
+ end
+
+ def test_glob_constraint_skip_route
+ get "/cars"
+ assert_equal "200", @response.code
+ end
+ def test_glob_constraint_skip_all
+ get "/missing"
+ assert_equal "404", @response.code
+ end
+end
+
class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 4e6a676fc6..9fb26e32b1 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -437,6 +437,11 @@ module RenderTestCases
@view.render(:partial => 'test/partial_with_layout_block_content', :layout => 'test/layout_for_partial', :locals => { :name => 'Foo!'})
end
+ def test_render_partial_with_layout_raises_descriptive_error
+ e = assert_raises(ActionView::MissingTemplate) { @view.render(partial: 'test/partial', layout: true) }
+ assert_match "Missing partial /true with", e.message
+ end
+
def test_render_with_nested_layout
assert_equal %(<title>title</title>\n\n<div id="column">column</div>\n<div id="content">content</div>\n),
@view.render(:file => "test/nested_layout", :layout => "layouts/yield")
diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb
index ed9d303158..8d32205fb8 100644
--- a/actionpack/test/template/template_test.rb
+++ b/actionpack/test/template/template_test.rb
@@ -82,8 +82,8 @@ class TestERBTemplate < ActiveSupport::TestCase
end
def test_text_template_does_not_html_escape
- @template = new_template("<%= apostrophe %>", format: :text)
- assert_equal "l'apostrophe", render
+ @template = new_template("<%= apostrophe %> <%== apostrophe %>", format: :text)
+ assert_equal "l'apostrophe l'apostrophe", render
end
def test_raw_template
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index c82d4f012c..963e52bff3 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -122,7 +122,7 @@ module ActiveModel
# Delete messages for +key+. Returns the deleted messages.
#
# person.errors.get(:name) # => ["can not be nil"]
- # person.errors.delete(:name) # => ["can not be nil"]
+ # person.errors.delete(:name) # => ["can not be nil"]
# person.errors.get(:name) # => nil
def delete(key)
messages.delete(key)
@@ -213,7 +213,7 @@ module ActiveModel
# Returns +true+ if no errors are found, +false+ otherwise.
# If the error message is a string it can be empty.
#
- # person.errors.full_messages # => ["name can not be nil"]
+ # person.errors.full_messages # => ["name can not be nil"]
# person.errors.empty? # => false
def empty?
all? { |k, v| v && v.empty? && !v.is_a?(String) }
@@ -246,7 +246,7 @@ module ActiveModel
to_hash(options && options[:full_messages])
end
- # Returns a Hash of attributes with their error messages. If +full_messages+
+ # Returns a Hash of attributes with their error messages. If +full_messages+
# is +true+, it will contain full messages (see +full_message+).
#
# person.to_hash # => {:name=>["can not be nil"]}
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index dfd68a90fd..fdb06aebb9 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -90,7 +90,7 @@ module ActiveModel
# person.name = 'bob'
# person.age = 22
# person.serializable_hash # => {"name"=>"bob", "age"=>22}
- # person.serializable_hash(only: :name) # => {"name"=>"bob"}
+ # person.serializable_hash(only: :name) # => {"name"=>"bob"}
# person.serializable_hash(except: :name) # => {"age"=>22}
# person.serializable_hash(methods: :capitalized_name)
# # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 629b157fed..ff3dfa01c8 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -109,8 +109,8 @@ module ActiveModel
# Return the kind for this validator.
#
- # PresenceValidator.new.kind # => :presence
- # UniquenessValidator.new.kind # => :uniqueness 
+ # PresenceValidator.new.kind # => :presence
+ # UniquenessValidator.new.kind # => :uniqueness
def kind
self.class.kind
end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index 293ce07f4e..1ffce1ae47 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -116,7 +116,7 @@ class ErrorsTest < ActiveModel::TestCase
test "added? should default message to :invalid" do
person = Person.new
- person.errors.add(:name, :invalid)
+ person.errors.add(:name)
assert person.errors.added?(:name)
end
@@ -161,7 +161,7 @@ class ErrorsTest < ActiveModel::TestCase
person = Person.new
person.errors.add(:name, "can not be blank")
person.errors.add(:name, "can not be nil")
- assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a
+ assert_equal ["name can not be blank", "name can not be nil"], person.errors.full_messages
end
test 'full_message should return the given message if attribute equals :base' do
@@ -240,4 +240,3 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.add_on_blank :name, :message => 'custom'
end
end
-
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 130d0f05d2..0a2e7a127c 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,8 +1,74 @@
## Rails 4.0.0 (unreleased) ##
+* Allow `Relation#where` with no arguments to be chained with new `not` query method.
+
+ Example:
+
+ Developer.where.not(name: 'Aaron')
+
+ *Akira Matsuda*
+
+* Unscope `update_column(s)` query to ignore default scope.
+
+ When applying `default_scope` to a class with a where clause, using
+ `update_column(s)` could generate a query that would not properly update
+ the record due to the where clause from the `default_scope` being applied
+ to the update query.
+
+ class User < ActiveRecord::Base
+ default_scope where(active: true)
+ end
+
+ user = User.first
+ user.active = false
+ user.save!
+
+ user.update_column(:active, true) # => false
+
+ In this situation we want to skip the default_scope clause and just
+ update the record based on the primary key. With this change:
+
+ user.update_column(:active, true) # => true
+
+ Fixes #8436.
+
+ *Carlos Antonio da Silva*
+
+* SQLite adapter no longer corrupts binary data if the data contains `%00`.
+
+ *Chris Feist*
+
+* Add migration history to `schema.rb` dump. Loading `schema.rb` with full migration
+ history restores the exact list of migrations that created that schema (including names
+ and fingerprints). This avoids possible mistakes caused by assuming all migrations with
+ a lower version have been run when loading `schema.rb`. Old `schema.rb` files without
+ migration history but with the `:version` setting still work as before.
+
+ *Josh Susser*
+
+* Add metadata columns to `schema_migrations` table. New columns are:
+
+ * `migrated_at`: timestamp
+ * `fingerprint`: md5 hash of migration source
+ * `name`: filename minus version and extension
+
+ *Josh Susser*
+
+* Fix performance problem with `primary_key` method in PostgreSQL adapter when having many schemas.
+ Uses `pg_constraint` table instead of `pg_depend` table which has many records in general.
+ Fix #8414
+
+ *kennyj*
+
+* Do not instantiate intermediate Active Record objects when eager loading.
+ These records caused `after_find` to run more than expected.
+ Fix #3313
+
+ *Yves Senn*
+
* Add STI support to init and building associations.
- Allows you to do BaseClass.new(:type => "SubClass") as well as
- parent.children.build(:type => "SubClass") or parent.build_child
+ Allows you to do `BaseClass.new(:type => "SubClass")` as well as
+ `parent.children.build(:type => "SubClass")` or `parent.build_child`
to initialize an STI subclass. Ensures that the class name is a
valid class and that it is in the ancestors of the super class
that the association is expecting.
@@ -776,7 +842,7 @@
end
person.pets.delete("1") # => [#<Pet id: 1>]
- person.pets.delete(2, 3) # => [#<Pet id: 2>, #<Pet id: 3>]
+ person.pets.delete(2, 3) # => [#<Pet id: 2>, #<Pet id: 3>]
*Francesco Rodriguez*
@@ -1047,11 +1113,11 @@
Note that you do not need to explicitly specify references in the
following cases, as they can be automatically inferred:
- Post.where(comments: { name: 'foo' })
- Post.where('comments.name' => 'foo')
- Post.order('comments.name')
+ Post.includes(:comments).where(comments: { name: 'foo' })
+ Post.includes(:comments).where('comments.name' => 'foo')
+ Post.includes(:comments).order('comments.name')
- You also do not need to worry about this unless you are doing eager
+ You do not need to worry about this unless you are doing eager
loading. Basically, don't worry unless you see a deprecation warning
or (in future releases) an SQL error due to a missing JOIN.
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 7f9628499c..7c43e37cf2 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -101,7 +101,7 @@ module ActiveRecord
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
#
- # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
+ # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
# # => [
# # #<Pet id: 2, name: "Spook">,
# # #<Pet id: 3, name: "Choo-Choo">
@@ -824,7 +824,7 @@ module ActiveRecord
#
# person.pets # => [#<Pet id: 20, name: "Snoop">]
#
- # person.pets.include?(Pet.find(20)) # => true
+ # person.pets.include?(Pet.find(20)) # => true
# person.pets.include?(Pet.find(21)) # => false
def include?(record)
@association.include?(record)
@@ -971,7 +971,7 @@ module ActiveRecord
# person.pets.reload # fetches pets from the database
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
#
- # person.pets(true)  # fetches pets from the database
+ # person.pets(true) # fetches pets from the database
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
def reload
proxy_association.reload
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 907fe70522..704998301c 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -32,8 +32,6 @@ module ActiveRecord
# autosave callbacks are executed. Placing your callbacks after
# associations is usually a good practice.
#
- # == Examples
- #
# === One-to-one Example
#
# class Post
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 7ec6abbc45..73012834c9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -190,16 +190,16 @@ module ActiveRecord
#
# What can be written like this with the regular calls to column:
#
- # create_table "products", force: true do |t|
- # t.column "shop_id", :integer
- # t.column "creator_id", :integer
- # t.column "name", :string, default: "Untitled"
- # t.column "value", :string, default: "Untitled"
- # t.column "created_at", :datetime
- # t.column "updated_at", :datetime
+ # create_table :products do |t|
+ # t.column :shop_id, :integer
+ # t.column :creator_id, :integer
+ # t.column :name, :string, default: "Untitled"
+ # t.column :value, :string, default: "Untitled"
+ # t.column :created_at, :datetime
+ # t.column :updated_at, :datetime
# end
#
- # Can also be written as follows using the short-hand:
+ # can also be written as follows using the short-hand:
#
# create_table :products do |t|
# t.integer :shop_id, :creator_id
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index f1e42dfbbe..1586e84a25 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -490,8 +490,8 @@ module ActiveRecord
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
ActiveRecord::SchemaMigration.order('version').map { |sm|
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
- }.join "\n\n"
+ "INSERT INTO #{sm_table} (version, migrated_at, fingerprint, name) VALUES ('#{sm.version}',CURRENT_TIMESTAMP,'#{sm.fingerprint}','#{sm.name}');"
+ }.join("\n\n")
end
# Should not be called normally, but this operation is non-destructive.
@@ -512,7 +512,7 @@ module ActiveRecord
end
unless migrated.include?(version)
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
+ ActiveRecord::SchemaMigration.create!(:version => version, :migrated_at => Time.now)
end
inserted = Set.new
@@ -520,7 +520,7 @@ module ActiveRecord
if inserted.include?(v)
raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
elsif v < version
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
+ ActiveRecord::SchemaMigration.create!(:version => v, :migrated_at => Time.now)
inserted << v
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 7c561b6f82..18bf14d1fb 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -267,7 +267,6 @@ module ActiveRecord
FROM pg_class seq,
pg_attribute attr,
pg_depend dep,
- pg_namespace name,
pg_constraint cons
WHERE seq.oid = dep.objid
AND seq.relkind = 'S'
@@ -306,12 +305,11 @@ module ActiveRecord
# Returns just a table's primary key
def primary_key(table)
row = exec_query(<<-end_sql, 'SCHEMA').rows.first
- SELECT DISTINCT(attr.attname)
+ SELECT attr.attname
FROM pg_attribute attr
- INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
WHERE cons.contype = 'p'
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
+ AND cons.conrelid = '#{quote_table_name(table)}'::regclass
end_sql
row && row.first
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index c53b7b3e78..81f92db271 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -79,16 +79,17 @@ module ActiveRecord
where(primary_key => id).update_all updates.join(', ')
end
- # Increment a number field by one, usually representing a count.
+ # Increment a numeric field by one, via a direct SQL update.
#
- # This is used for caching aggregate values, so that they don't need to be computed every time.
- # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
- # shown it would have to run an SQL query to find how many posts and comments there are.
+ # This method is used primarily for maintaining counter_cache columns used to
+ # store aggregate values. For example, a DiscussionBoard may cache posts_count
+ # and comments_count to avoid running an SQL query to calculate the number of
+ # posts and comments there are each time it is displayed.
#
# ==== Parameters
#
# * +counter_name+ - The name of the field that should be incremented.
- # * +id+ - The id of the object that should be incremented.
+ # * +id+ - The id of the object that should be incremented or an Array of ids.
#
# ==== Examples
#
@@ -98,14 +99,15 @@ module ActiveRecord
update_counters(id, counter_name => 1)
end
- # Decrement a number field by one, usually representing a count.
+ # Decrement a numeric field by one, via a direct SQL update.
#
- # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
+ # This works the same as increment_counter but reduces the column value by
+ # 1 instead of increasing it.
#
# ==== Parameters
#
# * +counter_name+ - The name of the field that should be decremented.
- # * +id+ - The id of the object that should be decremented.
+ # * +id+ - The id of the object that should be decremented or an Array of ids.
#
# ==== Examples
#
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index 23c272ef12..7bdc1bd4c6 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -29,8 +29,6 @@ module ActiveRecord
# Returns a cache key that can be used to identify this record.
#
- # ==== Examples
- #
# Product.new.cache_key # => "products/new"
# Product.find(5).cache_key # => "products/5" (updated_at not available)
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 22347fcaef..4ce276d4bf 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,5 +1,6 @@
require "active_support/core_ext/class/attribute_accessors"
require 'set'
+require 'digest/md5'
module ActiveRecord
# Exception that can be raised to stop migrations from going backwards.
@@ -554,6 +555,10 @@ module ActiveRecord
delegate :migrate, :announce, :write, :to => :migration
+ def fingerprint
+ @fingerprint ||= Digest::MD5.hexdigest(File.read(filename))
+ end
+
private
def migration
@@ -724,7 +729,7 @@ module ActiveRecord
raise UnknownMigrationVersionError.new(@target_version) if target.nil?
unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
target.migrate(@direction)
- record_version_state_after_migrating(target.version)
+ record_version_state_after_migrating(target)
end
end
@@ -747,7 +752,7 @@ module ActiveRecord
begin
ddl_transaction do
migration.migrate(@direction)
- record_version_state_after_migrating(migration.version)
+ record_version_state_after_migrating(migration)
end
rescue => e
canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
@@ -805,13 +810,18 @@ module ActiveRecord
raise DuplicateMigrationVersionError.new(version) if version
end
- def record_version_state_after_migrating(version)
+ def record_version_state_after_migrating(target)
if down?
- migrated.delete(version)
- ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
+ migrated.delete(target.version)
+ ActiveRecord::SchemaMigration.where(:version => target.version.to_s).delete_all
else
- migrated << version
- ActiveRecord::SchemaMigration.create!(:version => version.to_s)
+ migrated << target.version
+ ActiveRecord::SchemaMigration.create!(
+ :version => target.version.to_s,
+ :migrated_at => Time.now,
+ :fingerprint => target.fingerprint,
+ :name => File.basename(target.filename,'.rb').gsub(/^\d+_/,'')
+ )
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 94c109e72b..4d1a9c94b7 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -259,7 +259,7 @@ module ActiveRecord
verify_readonly_attribute(key.to_s)
end
- updated_count = self.class.where(self.class.primary_key => id).update_all(attributes)
+ updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
attributes.each do |k, v|
raw_write_attribute(k, v)
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index 38e18b32a4..df8654e5c1 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -4,6 +4,7 @@ module ActiveRecord
class QueryCache
module ClassMethods
# Enable the query cache within the block if Active Record is configured.
+ # If it's not, it will execute the given block.
def cache(&block)
if ActiveRecord::Base.connected?
connection.cache(&block)
@@ -13,6 +14,7 @@ module ActiveRecord
end
# Disable the query cache within the block if Active Record is configured.
+ # If it's not, it will execute the given block.
def uncached(&block)
if ActiveRecord::Base.connected?
connection.uncached(&block)
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 45f6a78428..5ddcaee6be 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -26,14 +26,13 @@ module ActiveRecord
# MySQL specific terms will lock you to using that particular database engine or require you to
# change your call if you switch engines.
#
- # ==== Examples
# # A simple SQL query spanning multiple tables
# Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
- # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
+ # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
#
# # You can use the same string replacement techniques as you can with ActiveRecord#find
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
- # > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
+ # # => [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
def find_by_sql(sql, binds = [])
logging_query_plan do
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
@@ -57,8 +56,6 @@ module ActiveRecord
#
# * +sql+ - An SQL statement which should return a count query from the database, see the example below.
#
- # ==== Examples
- #
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
def count_by_sql(sql)
logging_query_plan do
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 99e77e007a..ccc14dddeb 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -76,18 +76,17 @@ module ActiveRecord
#
# values = Person.group('last_name').maximum(:age)
# puts values["Drake"]
- # => 43
+ # # => 43
#
# drake = Family.find_by_last_name('Drake')
# values = Person.group(:family).maximum(:age) # Person belongs_to :family
# puts values[drake]
- # => 43
+ # # => 43
#
# values.each do |family, max_age|
# ...
# end
#
- # Examples:
# Person.calculate(:count, :all) # The same as Person.count
# Person.average(:age) # SELECT AVG(age) FROM people...
#
@@ -124,8 +123,6 @@ module ActiveRecord
# the plucked column names, if they can be deduced. Plucking an SQL fragment
# returns String values by default.
#
- # Examples:
- #
# Person.pluck(:id)
# # SELECT people.id FROM people
# # => [1, 2, 3]
@@ -182,8 +179,6 @@ module ActiveRecord
# Pluck all the ID's for the relation using the table's primary key
#
- # Examples:
- #
# Person.ids # SELECT people.id FROM people
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
def ids
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index eafe4a54c4..7ddaea1bb0 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -225,9 +225,11 @@ module ActiveRecord
orders = relation.order_values.map { |val| val.presence }.compact
values = @klass.connection.distinct("#{quoted_table_name}.#{primary_key}", orders)
- relation = relation.dup
+ relation = relation.dup.select(values)
+
+ id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
+ ids_array = id_rows.map {|row| row[primary_key]}
- ids_array = relation.select(values).collect {|row| row[primary_key]}
ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index a480ddec9e..f6d106f304 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -4,6 +4,51 @@ module ActiveRecord
module QueryMethods
extend ActiveSupport::Concern
+ # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
+ # In this case, #where must be chained with either #not, #like, or #not_like to return a new relation.
+ class WhereChain
+ def initialize(scope)
+ @scope = scope
+ end
+
+ # Returns a new relation expressing WHERE + NOT condition
+ # according to the conditions in the arguments.
+ #
+ # #not accepts conditions in one of these formats: String, Array, Hash.
+ # See #where for more details on each format.
+ #
+ # User.where.not("name = 'Jon'")
+ # # SELECT * FROM users WHERE name <> 'Jon'
+ #
+ # User.where.not(["name = ?", "Jon"])
+ # # SELECT * FROM users WHERE name <> 'Jon'
+ #
+ # User.where.not(name: "Jon")
+ # # SELECT * FROM users WHERE name <> 'Jon'
+ #
+ # User.where.not(name: nil)
+ # # SELECT * FROM users WHERE name IS NOT NULL
+ #
+ # User.where.not(name: %w(Ko1 Nobu))
+ # # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
+ def not(opts, *rest)
+ where_value = @scope.send(:build_where, opts, rest).map do |rel|
+ case rel
+ when Arel::Nodes::In
+ Arel::Nodes::NotIn.new(rel.left, rel.right)
+ when Arel::Nodes::Equality
+ Arel::Nodes::NotEqual.new(rel.left, rel.right)
+ when String
+ Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
+ else
+ Arel::Nodes::Not.new(rel)
+ end
+ end
+ @scope.where_values += where_value
+ @scope
+ end
+ end
+
Relation::MULTI_VALUE_METHODS.each do |name|
class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{name}_values # def select_values
@@ -370,18 +415,47 @@ module ActiveRecord
# User.joins(:posts).where({ "posts.published" => true })
# User.joins(:posts).where({ posts: { published: true } })
#
- # === empty condition
+ # === no argument or :chain
+ #
+ # If no argument or :chain is passed, #where returns a new instance of WhereChain which, when
+ # chained with either #not, #like, or #not_like, returns a new relation.
+ #
+ # User.where.not(name: "Jon")
+ # # SELECT * FROM users WHERE name <> 'Jon'
+ #
+ # Book.where.like(title: "Rails%")
+ # # SELECT * FROM books WHERE title LIKE 'Rails%'
+ #
+ # Conference.where.not_like(name: "%Kaigi")
+ # # SELECT * FROM conferences WHERE name NOT LIKE '%Kaigi'
#
- # If the condition returns true for blank?, then where is a no-op and returns the current relation.
- def where(opts, *rest)
- opts.blank? ? self : spawn.where!(opts, *rest)
+ # See WhereChain for more details on #not, #like, and #not_like.
+ #
+ # === blank condition
+ #
+ # If the condition is any blank-ish object, then #where is a no-op and returns
+ # the current relation.
+ def where(opts = :chain, *rest)
+ if opts == :chain
+ WhereChain.new(spawn)
+ elsif opts.blank?
+ self
+ else
+ spawn.where!(opts, *rest)
+ end
end
- def where!(opts, *rest) # :nodoc:
- references!(PredicateBuilder.references(opts)) if Hash === opts
+ # #where! is identical to #where, except that instead of returning a new relation, it adds
+ # the condition to the existing relation.
+ def where!(opts = :chain, *rest) # :nodoc:
+ if opts == :chain
+ WhereChain.new(self)
+ else
+ references!(PredicateBuilder.references(opts)) if Hash === opts
- self.where_values += build_where(opts, rest)
- self
+ self.where_values += build_where(opts, rest)
+ self
+ end
end
# Allows to specify a HAVING clause. Note that you can't use HAVING
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 352dee3826..d417e82548 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -12,9 +12,6 @@ module ActiveRecord
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
# Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
- #
- # ==== Examples
- #
# Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
# # Performs a single join query with both where conditions.
#
@@ -29,7 +26,6 @@ module ActiveRecord
# # => Post.where(published: true).joins(:comments)
#
# This is mainly intended for sharing common conditions between multiple associations.
- #
def merge(other)
if other.is_a?(Array)
to_a & other
@@ -51,11 +47,8 @@ module ActiveRecord
# Removes from the query the condition(s) specified in +skips+.
#
- # Example:
- #
# Post.order('id asc').except(:order) # discards the order condition
# Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
- #
def except(*skips)
result = Relation.new(klass, table, values.except(*skips))
result.default_scoped = default_scoped
@@ -65,11 +58,8 @@ module ActiveRecord
# Removes any condition from the query other than the one(s) specified in +onlies+.
#
- # Example:
- #
# Post.order('id asc').only(:where) # discards the order condition
# Post.order('id asc').only(:where, :order) # uses the specified order
- #
def only(*onlies)
result = Relation.new(klass, table, values.slice(*onlies))
result.default_scoped = default_scoped
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index eaa4aa7086..44b7eb424b 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -29,32 +29,55 @@ module ActiveRecord
# ActiveRecord::Schema is only supported by database adapters that also
# support migrations, the two features being very similar.
class Schema < Migration
+
+ # Returns the migrations paths.
+ #
+ # ActiveRecord::Schema.new.migrations_paths
+ # # => ["db/migrate"] # Rails migration path by default.
def migrations_paths
ActiveRecord::Migrator.migrations_paths
end
- def define(info, &block)
+ def define(info, &block) # :nodoc:
+ @using_deprecated_version_setting = info[:version].present?
+ SchemaMigration.drop_table
+ initialize_schema_migrations_table
+
instance_eval(&block)
- unless info[:version].blank?
- initialize_schema_migrations_table
- assume_migrated_upto_version(info[:version], migrations_paths)
- end
+ # handle files from pre-4.0 that used :version option instead of dumping migration table
+ assume_migrated_upto_version(info[:version], migrations_paths) if @using_deprecated_version_setting
end
# Eval the given block. All methods available to the current connection
# adapter are available within the block, so you can easily use the
# database definition DSL to build up your schema (+create_table+,
# +add_index+, etc.).
- #
- # The +info+ hash is optional, and if given is used to define metadata
- # about the current schema (currently, only the schema's version):
- #
- # ActiveRecord::Schema.define(version: 20380119000001) do
- # ...
- # end
def self.define(info={}, &block)
new.define(info, &block)
end
+
+ # Create schema migration history. Include migration statements in a block to this method.
+ #
+ # migrations do
+ # migration 20121128235959, "44f1397e3b92442ca7488a029068a5ad", "add_horn_color_to_unicorns"
+ # migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns"
+ # end
+ def migrations
+ raise(ArgumentError, "Can't set migrations while using :version option") if @using_deprecated_version_setting
+ yield
+ end
+
+ # Add a migration to the ActiveRecord::SchemaMigration table.
+ #
+ # The +version+ argument is an integer.
+ # The +fingerprint+ and +name+ arguments are required but may be empty strings.
+ # The migration's +migrated_at+ attribute is set to the current time,
+ # instead of being set explicitly as an argument to the method.
+ #
+ # migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns"
+ def migration(version, fingerprint, name)
+ SchemaMigration.create!(version: version, migrated_at: Time.now, fingerprint: fingerprint, name: name)
+ end
end
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 36bde44e7c..194a77ca16 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -24,6 +24,7 @@ module ActiveRecord
def dump(stream)
header(stream)
+ migrations(stream)
tables(stream)
trailer(stream)
stream
@@ -38,13 +39,11 @@ module ActiveRecord
end
def header(stream)
- define_params = @version ? "version: #{@version}" : ""
-
if stream.respond_to?(:external_encoding) && stream.external_encoding
stream.puts "# encoding: #{stream.external_encoding.name}"
end
- stream.puts <<HEADER
+ header_text = <<HEADER_RUBY
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
@@ -57,15 +56,27 @@ module ActiveRecord
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(#{define_params}) do
+ActiveRecord::Schema.define do
-HEADER
+HEADER_RUBY
+ stream.puts header_text
end
def trailer(stream)
stream.puts "end"
end
+ def migrations(stream)
+ all_migrations = ActiveRecord::SchemaMigration.all.to_a
+ if all_migrations.any?
+ stream.puts(" migrations do")
+ all_migrations.each do |migration|
+ stream.puts(migration.schema_line(" "))
+ end
+ stream.puts(" end\n\n")
+ end
+ end
+
def tables(stream)
@connection.tables.sort.each do |tbl|
next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index 9830abe7d8..6c3cd5b6ba 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -14,17 +14,38 @@ module ActiveRecord
end
def self.create_table
- unless connection.table_exists?(table_name)
+ if connection.table_exists?(table_name)
+ cols = connection.columns(table_name).collect { |col| col.name }
+ unless cols.include?("migrated_at")
+ connection.add_column(table_name, "migrated_at", :datetime)
+ q_table_name = connection.quote_table_name(table_name)
+ q_timestamp = connection.quoted_date(Time.now)
+ connection.update("UPDATE #{q_table_name} SET migrated_at = '#{q_timestamp}' WHERE migrated_at IS NULL")
+ connection.change_column(table_name, "migrated_at", :datetime, :null => false)
+ end
+ unless cols.include?("fingerprint")
+ connection.add_column(table_name, "fingerprint", :string, :limit => 32)
+ end
+ unless cols.include?("name")
+ connection.add_column(table_name, "name", :string)
+ end
+ else
connection.create_table(table_name, :id => false) do |t|
t.column :version, :string, :null => false
+ t.column :migrated_at, :datetime, :null => false
+ t.column :fingerprint, :string, :limit => 32
+ t.column :name, :string
end
- connection.add_index table_name, :version, :unique => true, :name => index_name
+ connection.add_index(table_name, "version", :unique => true, :name => index_name)
end
+ reset_column_information
end
def self.drop_table
if connection.table_exists?(table_name)
- connection.remove_index table_name, :name => index_name
+ if connection.index_exists?(table_name, "version", :unique => true, :name => index_name)
+ connection.remove_index(table_name, :name => index_name)
+ end
connection.drop_table(table_name)
end
end
@@ -32,5 +53,17 @@ module ActiveRecord
def version
super.to_i
end
+
+ # Construct ruby source to include in schema.rb dump for this migration.
+ # Pass a string of spaces as +indent+ to allow calling code to control how deeply indented the line is.
+ # The generated line includes the migration version, fingerprint, and name. Either fingerprint or name
+ # can be an empty string.
+ #
+ # Example output:
+ #
+ # migration 20121129235959, "ee4be703f9e6e2fc0f4baddebe6eb8f7", "add_magic_power_to_unicorns"
+ def schema_line(indent)
+ %Q(#{indent}migration %s, "%s", "%s") % [version, fingerprint, name]
+ end
end
end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index fb5f5b5be0..8b7eda6eee 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -116,7 +116,7 @@ module ActiveRecord
# Scopes can also be used while creating/building a record.
#
# class Article < ActiveRecord::Base
- # scope :published, -> { where(published: true) }
+ # scope :published, -> { where(published: true) }
# end
#
# Article.published.new.published # => true
@@ -126,7 +126,7 @@ module ActiveRecord
# on scopes. Assuming the following setup:
#
# class Article < ActiveRecord::Base
- # scope :published, -> { where(published: true) }
+ # scope :published, -> { where(published: true) }
# scope :featured, -> { where(featured: true) }
#
# def self.latest_article
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index b2eac0349b..bd47ba8741 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -46,4 +46,55 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
+ class ActiveRecordSchemaMigrationsTest < ActiveRecordSchemaTest
+ def setup
+ super
+ ActiveRecord::SchemaMigration.delete_all
+ end
+
+ def test_migration_adds_row_to_migrations_table
+ schema = ActiveRecord::Schema.new
+ schema.migration(1001, "", "")
+ schema.migration(1002, "123456789012345678901234567890ab", "add_magic_power_to_unicorns")
+
+ migrations = ActiveRecord::SchemaMigration.all.to_a
+ assert_equal 2, migrations.length
+
+ assert_equal 1001, migrations[0].version
+ assert_match %r{^2\d\d\d-}, migrations[0].migrated_at.to_s(:db)
+ assert_equal "", migrations[0].fingerprint
+ assert_equal "", migrations[0].name
+
+ assert_equal 1002, migrations[1].version
+ assert_match %r{^2\d\d\d-}, migrations[1].migrated_at.to_s(:db)
+ assert_equal "123456789012345678901234567890ab", migrations[1].fingerprint
+ assert_equal "add_magic_power_to_unicorns", migrations[1].name
+ end
+
+ def test_define_clears_schema_migrations
+ assert_nothing_raised do
+ ActiveRecord::Schema.define do
+ migrations do
+ migration(123001, "", "")
+ end
+ end
+ ActiveRecord::Schema.define do
+ migrations do
+ migration(123001, "", "")
+ end
+ end
+ end
+ end
+
+ def test_define_raises_if_both_version_and_explicit_migrations
+ assert_raise(ArgumentError) do
+ ActiveRecord::Schema.define(version: 123001) do
+ migrations do
+ migration(123001, "", "")
+ end
+ end
+ end
+ end
+ end
+
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 79612c8dee..94437380a4 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -944,6 +944,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal 3, Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
end
+ def test_dont_create_temporary_active_record_instances
+ Developer.instance_count = 0
+ developers = Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a
+ assert_equal developers.count, Developer.instance_count
+ end
+
def test_order_on_join_table_with_include_and_limit
assert_equal 5, Developer.all.merge!(:includes => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).to_a.size
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index d25aca760f..7e6c7d5862 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -298,12 +298,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, Firm.order(:id).find{|f| f.id > 0}.clients.length
end
- def test_find_with_blank_conditions
- [[], {}, nil, ""].each do |blank|
- assert_equal 2, Firm.all.merge!(:order => "id").first.clients.where(blank).to_a.size
- end
- end
-
def test_find_many_with_merged_options
assert_equal 1, companies(:first_firm).limited_clients.size
assert_equal 1, companies(:first_firm).limited_clients.to_a.size
diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb
index ee0c20747e..c2fdc50e52 100644
--- a/activerecord/test/cases/migration/logger_test.rb
+++ b/activerecord/test/cases/migration/logger_test.rb
@@ -10,6 +10,8 @@ module ActiveRecord
def migrate direction
# do nothing
end
+ def filename; "anon.rb"; end
+ def fingerprint; "123456789012345678901234567890ab"; end
end
def setup
diff --git a/activerecord/test/cases/migration/table_and_index_test.rb b/activerecord/test/cases/migration/table_and_index_test.rb
deleted file mode 100644
index 8fd770abd1..0000000000
--- a/activerecord/test/cases/migration/table_and_index_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- class Migration
- class TableAndIndexTest < ActiveRecord::TestCase
- def test_add_schema_info_respects_prefix_and_suffix
- conn = ActiveRecord::Base.connection
-
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
- # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
- ActiveRecord::Base.table_name_prefix = 'p_'
- ActiveRecord::Base.table_name_suffix = '_s'
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
-
- conn.initialize_schema_migrations_table
-
- assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
- ensure
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- end
- end
- end
-end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index c155f29973..3a861d887f 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -59,12 +59,21 @@ class MigrationTest < ActiveRecord::TestCase
def test_migrator_versions
migrations_path = MIGRATIONS_ROOT + "/valid"
ActiveRecord::Migrator.migrations_paths = migrations_path
+ m0_path = File.join(migrations_path, "1_valid_people_have_last_names.rb")
+ m0_fingerprint = Digest::MD5.hexdigest(File.read(m0_path))
ActiveRecord::Migrator.up(migrations_path)
assert_equal 3, ActiveRecord::Migrator.current_version
assert_equal 3, ActiveRecord::Migrator.last_version
assert_equal false, ActiveRecord::Migrator.needs_migration?
+ rows = connection.select_all("SELECT * FROM #{connection.quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)}")
+ assert_equal m0_fingerprint, rows[0]["fingerprint"]
+ assert_equal "valid_people_have_last_names", rows[0]["name"]
+ rows.each do |row|
+ assert_match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, row["migrated_at"].to_s, "missing migrated_at") # sometimes a String, sometimes a Time
+ end
+
ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
assert_equal 0, ActiveRecord::Migrator.current_version
assert_equal 3, ActiveRecord::Migrator.last_version
@@ -337,7 +346,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_nothing_raised {
Person.connection.create_table :binary_testings do |t|
- t.column "data", :binary, :null => false
+ t.column :data, :binary, :null => false
end
}
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 1e16addcf3..0f0384382f 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -18,6 +18,9 @@ module ActiveRecord
def up; @went_up = true; end
def down; @went_down = true; end
+ # also used in place of a MigrationProxy
+ def filename; "anon.rb"; end
+ def fingerprint; "123456789012345678901234567890ab"; end
end
def setup
@@ -102,7 +105,7 @@ module ActiveRecord
end
def test_finds_pending_migrations
- ActiveRecord::SchemaMigration.create!(:version => '1')
+ ActiveRecord::SchemaMigration.create!(:version => '1', :name => "anon", :migrated_at => Time.now)
migration_list = [ Migration.new('foo', 1), Migration.new('bar', 3) ]
migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
@@ -152,7 +155,7 @@ module ActiveRecord
end
def test_current_version
- ActiveRecord::SchemaMigration.create!(:version => '1000')
+ ActiveRecord::SchemaMigration.create!(:version => '1000', :name => "anon", :migrated_at => Time.now)
assert_equal 1000, ActiveRecord::Migrator.current_version
end
@@ -320,7 +323,7 @@ module ActiveRecord
def test_only_loads_pending_migrations
# migrate up to 1
- ActiveRecord::SchemaMigration.create!(:version => '1')
+ ActiveRecord::SchemaMigration.create!(:version => '1', :name => "anon", :migrated_at => Time.now)
calls, migrator = migrator_class(3)
migrator.migrate("valid", nil)
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 02034c87b4..9e0423ab52 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -512,6 +512,14 @@ class PersistencesTest < ActiveRecord::TestCase
assert_equal 'super_title', t.title
end
+ def test_update_column_with_default_scope
+ developer = DeveloperCalledDavid.first
+ developer.name = 'John'
+ developer.save!
+
+ assert developer.update_column(:name, 'Will'), 'did not update record due to default scope'
+ end
+
def test_update_columns
topic = Topic.find(1)
topic.update_columns({ "approved" => true, title: "Sebastian Topic" })
@@ -616,6 +624,14 @@ class PersistencesTest < ActiveRecord::TestCase
assert_equal true, topic.update_columns(title: "New title")
end
+ def test_update_columns_with_default_scope
+ developer = DeveloperCalledDavid.first
+ developer.name = 'John'
+ developer.save!
+
+ assert developer.update_columns(name: 'Will'), 'did not update record due to default scope'
+ end
+
def test_update_attributes
topic = Topic.find(1)
assert !topic.approved?
diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb
new file mode 100644
index 0000000000..8ce44636b4
--- /dev/null
+++ b/activerecord/test/cases/relation/where_chain_test.rb
@@ -0,0 +1,75 @@
+require 'cases/helper'
+require 'models/post'
+require 'models/comment'
+
+module ActiveRecord
+ class WhereChainTest < ActiveRecord::TestCase
+ fixtures :posts
+
+ def test_not_eq
+ expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'hello')
+ relation = Post.where.not(title: 'hello')
+ assert_equal([expected], relation.where_values)
+ end
+
+ def test_not_null
+ expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], nil)
+ relation = Post.where.not(title: nil)
+ assert_equal([expected], relation.where_values)
+ end
+
+ def test_not_in
+ expected = Arel::Nodes::NotIn.new(Post.arel_table[:title], %w[hello goodbye])
+ relation = Post.where.not(title: %w[hello goodbye])
+ assert_equal([expected], relation.where_values)
+ end
+
+ def test_association_not_eq
+ expected = Arel::Nodes::NotEqual.new(Comment.arel_table[:title], 'hello')
+ relation = Post.joins(:comments).where.not(comments: {title: 'hello'})
+ assert_equal(expected.to_sql, relation.where_values.first.to_sql)
+ end
+
+ def test_not_eq_with_preceding_where
+ relation = Post.where(title: 'hello').where.not(title: 'world')
+
+ expected = Arel::Nodes::Equality.new(Post.arel_table[:title], 'hello')
+ assert_equal(expected, relation.where_values.first)
+
+ expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'world')
+ assert_equal(expected, relation.where_values.last)
+ end
+
+ def test_not_eq_with_succeeding_where
+ relation = Post.where.not(title: 'hello').where(title: 'world')
+
+ expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'hello')
+ assert_equal(expected, relation.where_values.first)
+
+ expected = Arel::Nodes::Equality.new(Post.arel_table[:title], 'world')
+ assert_equal(expected, relation.where_values.last)
+ end
+
+ def test_not_eq_with_string_parameter
+ expected = Arel::Nodes::Not.new("title = 'hello'")
+ relation = Post.where.not("title = 'hello'")
+ assert_equal([expected], relation.where_values)
+ end
+
+ def test_not_eq_with_array_parameter
+ expected = Arel::Nodes::Not.new("title = 'hello'")
+ relation = Post.where.not(['title = ?', 'hello'])
+ assert_equal([expected], relation.where_values)
+ end
+
+ def test_chaining_multiple
+ relation = Post.where.not(author_id: [1, 2]).where.not(title: 'ruby on rails')
+
+ expected = Arel::Nodes::NotIn.new(Post.arel_table[:author_id], [1, 2])
+ assert_equal(expected, relation.where_values[0])
+
+ expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'ruby on rails')
+ assert_equal(expected, relation.where_values[1])
+ end
+ end
+end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 9c0b139dbf..297e865308 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -85,5 +85,11 @@ module ActiveRecord
def test_where_with_empty_hash_and_no_foreign_key
assert_equal 0, Edge.where(:sink => {}).count
end
+
+ def test_where_with_blank_conditions
+ [[], {}, nil, ""].each do |blank|
+ assert_equal 4, Edge.where(blank).order("sink_id").to_a.size
+ end
+ end
end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 7ff0044bd4..264846eedb 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -1,6 +1,5 @@
require "cases/helper"
-
class SchemaDumperTest < ActiveRecord::TestCase
def setup
super
@@ -18,11 +17,15 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_dump_schema_information_outputs_lexically_ordered_versions
versions = %w{ 20100101010101 20100201010101 20100301010101 }
versions.reverse.each do |v|
- ActiveRecord::SchemaMigration.create!(:version => v)
+ ActiveRecord::SchemaMigration.create!(
+ :version => v, :migrated_at => Time.now,
+ :fingerprint => "123456789012345678901234567890ab", :name => "anon")
end
schema_info = ActiveRecord::Base.connection.dump_schema_information
assert_match(/20100201010101.*20100301010101/m, schema_info)
+ target_line = %q{INSERT INTO schema_migrations (version, migrated_at, fingerprint, name) VALUES ('20100101010101',CURRENT_TIMESTAMP,'123456789012345678901234567890ab','anon');}
+ assert_match target_line, schema_info
end
def test_magic_comment
@@ -36,6 +39,16 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_no_match %r{create_table "schema_migrations"}, output
end
+ def test_schema_dump_includes_migrations
+ ActiveRecord::SchemaMigration.delete_all
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/always_safe")
+
+ output = standard_dump
+ assert_match %r{migrations do}, output, "Missing migrations block"
+ assert_match %r{migration 1001, "[0-9a-f]{32}", "always_safe"}, output, "Missing migration line"
+ assert_match %r{migration 1002, "[0-9a-f]{32}", "still_safe"}, output, "Missing migration line"
+ end
+
def test_schema_dump_excludes_sqlite_sequence
output = standard_dump
assert_no_match %r{create_table "sqlite_sequence"}, output
diff --git a/activerecord/test/cases/schema_migration_test.rb b/activerecord/test/cases/schema_migration_test.rb
new file mode 100644
index 0000000000..882067a7d4
--- /dev/null
+++ b/activerecord/test/cases/schema_migration_test.rb
@@ -0,0 +1,54 @@
+require "cases/helper"
+
+class SchemaMigrationTest < ActiveRecord::TestCase
+ def sm_table_name
+ ActiveRecord::SchemaMigration.table_name
+ end
+
+ def connection
+ ActiveRecord::Base.connection
+ end
+
+ def test_add_schema_info_respects_prefix_and_suffix
+ connection.drop_table(sm_table_name) if connection.table_exists?(sm_table_name)
+ # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
+ ActiveRecord::Base.table_name_prefix = 'p_'
+ ActiveRecord::Base.table_name_suffix = '_s'
+ connection.drop_table(sm_table_name) if connection.table_exists?(sm_table_name)
+
+ ActiveRecord::SchemaMigration.create_table
+
+ assert_equal "p_unique_schema_migrations_s", connection.indexes(sm_table_name)[0][:name]
+ ensure
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ end
+
+ def test_add_metadata_columns_to_exisiting_schema_migrations
+ # creates the old table schema from pre-Rails4.0, so we can test adding to it below
+ if connection.table_exists?(sm_table_name)
+ connection.drop_table(sm_table_name)
+ end
+ connection.create_table(sm_table_name, :id => false) do |schema_migrations_table|
+ schema_migrations_table.column("version", :string, :null => false)
+ end
+
+ connection.insert "INSERT INTO #{connection.quote_table_name(sm_table_name)} (version) VALUES (100)"
+ connection.insert "INSERT INTO #{connection.quote_table_name(sm_table_name)} (version) VALUES (200)"
+
+ ActiveRecord::SchemaMigration.create_table
+
+ rows = connection.select_all("SELECT * FROM #{connection.quote_table_name(sm_table_name)}")
+ assert rows[0].has_key?("migrated_at"), "missing column `migrated_at`"
+ assert_match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, rows[0]["migrated_at"].to_s) # sometimes a String, sometimes a Time
+ assert rows[0].has_key?("fingerprint"), "missing column `fingerprint`"
+ assert rows[0].has_key?("name"), "missing column `name`"
+ end
+
+ def test_schema_migrations_columns
+ ActiveRecord::SchemaMigration.create_table
+
+ columns = connection.columns(sm_table_name).collect(&:name)
+ %w[version migrated_at fingerprint name].each { |col| assert columns.include?(col), "missing column `#{col}`" }
+ end
+end
diff --git a/activerecord/test/migrations/always_safe/1001_always_safe.rb b/activerecord/test/migrations/always_safe/1001_always_safe.rb
new file mode 100644
index 0000000000..454b972507
--- /dev/null
+++ b/activerecord/test/migrations/always_safe/1001_always_safe.rb
@@ -0,0 +1,5 @@
+class AlwaysSafe < ActiveRecord::Migration
+ def change
+ # do nothing to avoid side-effect conflicts from running multiple times
+ end
+end
diff --git a/activerecord/test/migrations/always_safe/1002_still_safe.rb b/activerecord/test/migrations/always_safe/1002_still_safe.rb
new file mode 100644
index 0000000000..7398ae27a2
--- /dev/null
+++ b/activerecord/test/migrations/always_safe/1002_still_safe.rb
@@ -0,0 +1,5 @@
+class StillSafe < ActiveRecord::Migration
+ def change
+ # do nothing to avoid side-effect conflicts from running multiple times
+ end
+end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 622dd75aeb..1c048bb6b4 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -57,6 +57,16 @@ class Developer < ActiveRecord::Base
def log=(message)
audit_logs.build :message => message
end
+
+ after_find :track_instance_count
+ cattr_accessor :instance_count
+
+ def track_instance_count
+ self.class.instance_count ||= 0
+ self.class.instance_count += 1
+ end
+ private :track_instance_count
+
end
class AuditLog < ActiveRecord::Base
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 450669968b..f5f2aa85ef 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,11 @@
## Rails 4.0.0 (unreleased) ##
+* Deprecate `ActiveSupport::BasicObject` in favor of `ActiveSupport::ProxyObject`.
+ This class is used for proxy classes. It avoids confusion with Ruby's BasicObject
+ class.
+
+ *Francesco Rodriguez*
+
* Patched Marshal#load to work with constant autoloading.
Fixes autoloading with cache stores that relay on Marshal(MemCacheStore and FileStore). [fixes #8167]
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 4e397ea110..b602686114 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -40,6 +40,7 @@ module ActiveSupport
eager_autoload do
autoload :BacktraceCleaner
autoload :BasicObject
+ autoload :ProxyObject
autoload :Benchmarkable
autoload :Cache
autoload :Callbacks
diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb
index 6ccb0cd525..242b766b58 100644
--- a/activesupport/lib/active_support/basic_object.rb
+++ b/activesupport/lib/active_support/basic_object.rb
@@ -1,13 +1,7 @@
-module ActiveSupport
- # A class with no predefined methods that behaves similarly to Builder's
- # BlankSlate. Used for proxy classes.
- class BasicObject < ::BasicObject
- undef_method :==
- undef_method :equal?
+require 'active_support/deprecation'
- # Let ActiveSupport::BasicObject at least raise exceptions.
- def raise(*args)
- ::Object.send(:raise, *args)
- end
- end
+module ActiveSupport
+ # :nodoc:
+ # Deprecated in favor of ActiveSupport::ProxyObject
+ BasicObject = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::BasicObject', 'ActiveSupport::ProxyObject')
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 9a53870b3d..fdec2de1d5 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -582,7 +582,7 @@ module ActiveSupport
end
# Returns the size of the cached value. This could be less than
- # <tt>value.size</tt> if the data is compressed.
+ # <tt>value.size</tt> if the data is compressed.
def size
if defined?(@s)
@s
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 8199f431f1..3a8353857e 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -14,7 +14,7 @@ module ActiveSupport
# Mixing in this module allows you to define the events in the object's
# lifecycle that will support callbacks (via +ClassMethods.define_callbacks+),
# set the instance methods, procs, or callback objects to be called (via
- # +ClassMethods.set_callback+), and run the installed callbacks at the
+ # +ClassMethods.set_callback+), and run the installed callbacks at the
# appropriate times (via +run_callbacks+).
#
# Three kinds of callbacks are supported: before callbacks, run before a
@@ -382,7 +382,7 @@ module ActiveSupport
# set_callback :save, :before_meth
#
# The callback can specified as a symbol naming an instance method; as a
- # proc, lambda, or block; as a string to be instance evaluated; or as an
+ # proc, lambda, or block; as a string to be instance evaluated; or as an
# object that responds to a certain method determined by the <tt>:scope</tt>
# argument to +define_callback+.
#
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 16d2a6a290..e0d39d509f 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -38,7 +38,7 @@ module ActiveSupport
end
# Allows you to add shortcut so that you don't have to refer to attribute
- # through config. Also look at the example for config to contrast.
+ # through config. Also look at the example for config to contrast.
#
# Defines both class and instance config accessors.
#
@@ -75,7 +75,7 @@ module ActiveSupport
# end
#
# User.allowed_access = false
- #  User.allowed_access # => false
+ # User.allowed_access # => false
#
# User.new.allowed_access = true # => NoMethodError
# User.new.allowed_access # => NoMethodError
@@ -88,7 +88,7 @@ module ActiveSupport
# end
#
# User.allowed_access = false
- #  User.allowed_access # => false
+ # User.allowed_access # => false
#
# User.new.allowed_access = true # => NoMethodError
# User.new.allowed_access # => NoMethodError
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index ff06436bd6..64e9945ef5 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -26,7 +26,7 @@ class Array
#
# [].to_sentence # => ""
# ['one'].to_sentence # => "one"
- # ['one', 'two'].to_sentence # => "one and two"
+ # ['one', 'two'].to_sentence # => "one and two"
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
#
# ['one', 'two'].to_sentence(passing: 'invalid option')
@@ -41,7 +41,7 @@ class Array
# Examples using <tt>:locale</tt> option:
#
# # Given this locale dictionary:
- # # 
+ # #
# # es:
# # support:
# # array:
@@ -53,7 +53,7 @@ class Array
# # => "uno y dos"
#
# ['uno', 'dos', 'tres'].to_sentence(locale: :es)
- # # => "uno o dos o al menos tres"
+ # # => "uno o dos o al menos tres"
def to_sentence(options = {})
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index 1c3d26ead4..1504e18839 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -72,6 +72,9 @@ class Class
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
+ # We use class_eval here rather than define_method because class_attribute
+ # may be used in a performance sensitive context therefore the overhead that
+ # define_method introduces may become significant.
attrs.each do |name|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def self.#{name}() nil end
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index c2e0ebb3d4..9a2dc6e7c5 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -31,7 +31,7 @@ class Class
# class Bar < Foo; end
# class Baz < Foo; end
#
- # Foo.subclasses # => [Baz, Bar]
+ # Foo.subclasses # => [Baz, Bar]
def subclasses
subclasses, chain = [], descendants
chain.each do |k|
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 85b0e10be2..e1ce9f371a 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -88,6 +88,19 @@ class Hash
end
class << self
+ # Returns a Hash containing a collection of pairs when the key is the node name and the value is
+ # its content
+ #
+ # xml = <<-XML
+ # <?xml version="1.0" encoding="UTF-8"?>
+ # <hash>
+ # <foo type="integer">1</foo>
+ # <bar type="integer">2</bar>
+ # </hash>
+ # XML
+ #
+ # hash = Hash.from_xml(xml)
+ # # => {"hash"=>{"foo"=>1, "bar"=>2}}
def from_xml(xml)
typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)))
end
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index 6c7e876fca..981e8436bf 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -16,7 +16,7 @@ class Hash
# converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
# desirable.
#
- # b = { b: 1 }
+ # b = { b: 1 }
# { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
alias nested_under_indifferent_access with_indifferent_access
end
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index 13081995b0..b4c451ace4 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -24,7 +24,7 @@ class Hash
# Return a new hash with all keys converted to strings.
#
- # hash = { name: 'Rob', age: '28' }
+ # hash = { name: 'Rob', age: '28' }
#
# hash.stringify_keys
# #=> { "name" => "Rob", "age" => "28" }
@@ -44,7 +44,7 @@ class Hash
# hash = { 'name' => 'Rob', 'age' => '28' }
#
# hash.symbolize_keys
- # #=> { name: "Rob", age: "28" }
+ # #=> { name: "Rob", age: "28" }
def symbolize_keys
transform_keys{ |key| key.to_sym rescue key }
end
@@ -102,7 +102,7 @@ class Hash
# This includes the keys from the root hash and from all
# nested hashes.
#
- # hash = { person: { name: 'Rob', age: '28' } }
+ # hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_stringify_keys
# # => { "person" => { "name" => "Rob", "age" => "28" } }
@@ -121,10 +121,10 @@ class Hash
# they respond to +to_sym+. This includes the keys from the root hash
# and from all nested hashes.
#
- # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
#
# hash.deep_symbolize_keys
- # # => { person: { name: "Rob", age: "28" } }
+ # # => { person: { name: "Rob", age: "28" } }
def deep_symbolize_keys
deep_transform_keys{ |key| key.to_sym rescue key }
end
diff --git a/activesupport/lib/active_support/core_ext/object/deep_dup.rb b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
index f55fbc282e..1d639f3af6 100644
--- a/activesupport/lib/active_support/core_ext/object/deep_dup.rb
+++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
@@ -32,7 +32,7 @@ end
class Hash
# Returns a deep copy of hash.
#
- # hash = { a: { b: 'b' } }
+ # hash = { a: { b: 'b' } }
# dup = hash.deep_dup
# dup[:a][:c] = 'c'
#
diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb
index 1bf622d6a6..497c4c3fb8 100644
--- a/activesupport/lib/active_support/core_ext/time/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/time/marshal.rb
@@ -24,7 +24,7 @@ if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
def _dump(*args)
obj = dup
obj.instance_variable_set('@_zone', zone)
- obj._dump_without_zone(*args)
+ obj.send :_dump_without_zone, *args
end
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index b816ecae5a..efd351d741 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -44,7 +44,7 @@ module ActiveSupport #:nodoc:
self.autoload_once_paths = []
# An array of qualified constant names that have been loaded. Adding a name
- # to this array will cause it to be unloaded the next time Dependencies are
+ # to this array will cause it to be unloaded the next time Dependencies are
# cleared.
mattr_accessor :autoloaded_constants
self.autoloaded_constants = []
@@ -344,7 +344,7 @@ module ActiveSupport #:nodoc:
# Given +path+, a filesystem path to a ruby file, return an array of
# constant paths which would cause Dependencies to attempt to load this
- # file.
+ # file.
def loadable_constants_for_path(path, bases = autoload_paths)
path = $` if path =~ /\.rb\z/
expanded_path = File.expand_path(path)
@@ -394,7 +394,7 @@ module ActiveSupport #:nodoc:
# Attempt to autoload the provided module name by searching for a directory
# matching the expected path suffix. If found, the module is created and
# assigned to +into+'s constants with the name +const_name+. Provided that
- # the directory was loaded from a reloadable base path, it is added to the
+ # the directory was loaded from a reloadable base path, it is added to the
# set of constants that are to be unloaded.
def autoload_module!(into, const_name, qualified_name, path_suffix)
return nil unless base_path = autoloadable_module?(path_suffix)
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 7e99646117..2cb1f408b6 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -1,4 +1,4 @@
-require 'active_support/basic_object'
+require 'active_support/proxy_object'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/object/acts_like'
@@ -7,7 +7,7 @@ module ActiveSupport
# Time#advance, respectively. It mainly supports the methods on Numeric.
#
# 1.month.ago # equivalent to Time.now.advance(months: -1)
- class Duration < BasicObject
+ class Duration < ProxyObject
attr_accessor :value, :parts
def initialize(value, parts) #:nodoc:
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index a6b9aa3503..20136dd1b0 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -68,7 +68,7 @@ module ActiveSupport
end
# Executes the given block and updates the latest watched files and
- # timestamp.
+ # timestamp.
def execute
@last_watched = watched
@last_update_at = updated_at(@last_watched)
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index af506d6f2e..6f259a093b 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -36,7 +36,7 @@ module ActiveSupport
end
# Private, for the test suite.
- def initialize_dup(orig) # :nodoc:
+ def initialize_dup(orig) # :nodoc:
%w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
instance_variable_set("@#{scope}", orig.send(scope).dup)
end
@@ -44,7 +44,7 @@ module ActiveSupport
# Specifies a new acronym. An acronym must be specified as it will appear
# in a camelized string. An underscore string that contains the acronym
- # will retain the acronym when passed to +camelize+, +humanize+, or
+ # will retain the acronym when passed to +camelize+, +humanize+, or
# +titleize+. A camelized string that contains the acronym will maintain
# the acronym when titleized or humanized, and will convert the acronym
# into a non-delimited single lowercase word when passed to +underscore+.
@@ -79,7 +79,7 @@ module ActiveSupport
#
# +acronym+ may be used to specify any word that contains an acronym or
# otherwise needs to maintain a non-standard capitalization. The only
- # restriction is that the word must begin with a capital letter.
+ # restriction is that the word must begin with a capital letter.
#
# acronym 'RESTful'
# underscore 'RESTful' #=> 'restful'
@@ -97,7 +97,7 @@ module ActiveSupport
end
# Specifies a new pluralization rule and its replacement. The rule can
- # either be a string or a regular expression. The replacement should
+ # either be a string or a regular expression. The replacement should
# always be a string that may include references to the matched data from
# the rule.
def plural(rule, replacement)
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 1588674afc..b7dc0689b0 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -33,7 +33,7 @@ module ActiveSupport
# the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
# bits. If you are using a user-entered secret, you can generate a suitable
# key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
- # similar.
+ # similar.
#
# Options:
# * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
@@ -50,7 +50,7 @@ module ActiveSupport
end
# Encrypt and sign a message. We need to sign the message in order to avoid
- # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
+ # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
def encrypt_and_sign(value)
verifier.generate(_encrypt(value))
end
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 140b6ca08d..a87383fe99 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -6,7 +6,7 @@ module ActiveSupport
# signed to prevent tampering.
#
# This is useful for cases like remember-me tokens and auto-unsubscribe links
- # where the session store isn't suitable or available.
+ # where the session store isn't suitable or available.
#
# Remember Me:
# cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now])
diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb
index 1bf8e618ad..ffebd9a60b 100644
--- a/activesupport/lib/active_support/multibyte.rb
+++ b/activesupport/lib/active_support/multibyte.rb
@@ -5,7 +5,7 @@ module ActiveSupport #:nodoc:
# The proxy class returned when calling mb_chars. You can use this accessor
# to configure your own proxy class so you can support other encodings. See
- # the ActiveSupport::Multibyte::Chars implementation for an example how to
+ # the ActiveSupport::Multibyte::Chars implementation for an example how to
# do this.
#
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
diff --git a/activesupport/lib/active_support/proxy_object.rb b/activesupport/lib/active_support/proxy_object.rb
new file mode 100644
index 0000000000..a2bdf1d790
--- /dev/null
+++ b/activesupport/lib/active_support/proxy_object.rb
@@ -0,0 +1,13 @@
+module ActiveSupport
+ # A class with no predefined methods that behaves similarly to Builder's
+ # BlankSlate. Used for proxy classes.
+ class ProxyObject < ::BasicObject
+ undef_method :==
+ undef_method :equal?
+
+ # Let ActiveSupport::BasicObject at least raise exceptions.
+ def raise(*args)
+ ::Object.send(:raise, *args)
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 33810442da..18bc919734 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -11,7 +11,7 @@ module ActiveSupport
# logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
#
# This is used by the default Rails.logger as configured by Railties to make
- # it easy to stamp log lines with subdomains, request ids, and anything else
+ # it easy to stamp log lines with subdomains, request ids, and anything else
# to aid debugging of multi-user production applications.
module TaggedLogging
module Formatter # :nodoc:
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index ee7bda9f93..e4f182a3aa 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -69,7 +69,7 @@ module ActiveSupport
alias :assert_no_match :refute_match
alias :assert_not_same :refute_same
- # Fails if the block raises an exception.
+ # Fails if the block raises an exception.
#
# assert_nothing_raised do
# ...
diff --git a/activesupport/lib/active_support/testing/constant_lookup.rb b/activesupport/lib/active_support/testing/constant_lookup.rb
index 73e87befb6..52bfeb7179 100644
--- a/activesupport/lib/active_support/testing/constant_lookup.rb
+++ b/activesupport/lib/active_support/testing/constant_lookup.rb
@@ -30,7 +30,7 @@ module ActiveSupport
module ConstantLookup
extend ::ActiveSupport::Concern
- module ClassMethods
+ module ClassMethods # :nodoc:
def determine_constant_from_test_name(test_name)
names = test_name.split "::"
while names.size > 0 do
diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb
index 944806bb64..b04bbbbaea 100644
--- a/activesupport/lib/active_support/testing/pending.rb
+++ b/activesupport/lib/active_support/testing/pending.rb
@@ -2,8 +2,8 @@ require 'active_support/deprecation'
module ActiveSupport
module Testing
- module Pending
- unless defined?(Spec)
+ module Pending # :nodoc:
+ unless defined?(Spec)
def pending(description = "", &block)
ActiveSupport::Deprecation.warn("#pending is deprecated and will be removed in Rails 4.1, please use #skip instead.")
skip(description.blank? ? nil : description)
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 1a37d6f2a4..d087955587 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -285,9 +285,9 @@ module ActiveSupport
parts.fetch(:year, now.year),
parts.fetch(:mon, now.month),
parts.fetch(:mday, now.day),
- parts.fetch(:hour, now.hour),
- parts.fetch(:min, now.min),
- parts.fetch(:sec, now.sec) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
parts.fetch(:offset, 0)
)
@@ -326,7 +326,7 @@ module ActiveSupport
end
# Available so that TimeZone instances respond like TZInfo::Timezone
- # instances.
+ # instances.
def period_for_utc(time)
tzinfo.period_for_utc(time)
end
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index c8312aa653..2826f51f2d 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -21,7 +21,7 @@ class DurationTest < ActiveSupport::TestCase
assert ActiveSupport::Duration === 1.day
assert !(ActiveSupport::Duration === 1.day.to_i)
assert !(ActiveSupport::Duration === 'foo')
- assert !(ActiveSupport::Duration === ActiveSupport::BasicObject.new)
+ assert !(ActiveSupport::Duration === ActiveSupport::ProxyObject.new)
end
def test_equals
@@ -131,7 +131,7 @@ class DurationTest < ActiveSupport::TestCase
assert_equal Time.local(2009,3,29,0,0,0) + 1.day, Time.local(2009,3,30,0,0,0)
end
end
-
+
def test_delegation_with_block_works
counter = 0
assert_nothing_raised do
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 952c82f9d8..51a7e4b2fe 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -923,8 +923,10 @@ class DependenciesTest < ActiveSupport::TestCase
def test_remove_constant_does_not_autoload_already_removed_parents_as_a_side_effect
with_autoloading_fixtures do
- ::A
- ::A::B
+ silence_warnings do
+ ::A
+ ::A::B
+ end
ActiveSupport::Dependencies.remove_constant('A')
ActiveSupport::Dependencies.remove_constant('A::B')
assert !defined?(A)
@@ -934,7 +936,9 @@ class DependenciesTest < ActiveSupport::TestCase
def test_load_once_constants_should_not_be_unloaded
with_autoloading_fixtures do
ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths
- ::A.to_s
+ silence_warnings do
+ ::A
+ end
assert defined?(A)
ActiveSupport::Dependencies.clear
assert defined?(A)
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 0088a06c34..2bf73291a2 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -47,7 +47,10 @@ class MultibyteCharsTest < ActiveSupport::TestCase
end
def test_methods_are_forwarded_to_wrapped_string_for_byte_strings
+ original_encoding = BYTE_STRING.encoding
assert_equal BYTE_STRING.length, BYTE_STRING.mb_chars.length
+ ensure
+ BYTE_STRING.force_encoding(original_encoding)
end
def test_forwarded_method_with_non_string_result_should_be_returned_vertabim
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index bdeb80a294..9c3b5d0667 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -219,6 +219,13 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_parse_with_missing_time_components
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ zone.stubs(:now).returns zone.local(1999, 12, 31, 12, 59, 59)
+ twz = zone.parse('2012-12-01')
+ assert_equal Time.utc(2012, 12, 1), twz.time
+ end
+
def test_parse_with_javascript_date
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
twz = zone.parse("Mon May 28 2012 00:00:00 GMT-0700 (PDT)")
diff --git a/guides/assets/images/rails4_features.png b/guides/assets/images/rails4_features.png
new file mode 100644
index 0000000000..a979f02207
--- /dev/null
+++ b/guides/assets/images/rails4_features.png
Binary files differ
diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb
index 3b124ef236..a53d34a279 100644
--- a/guides/rails_guides/generator.rb
+++ b/guides/rails_guides/generator.rb
@@ -84,7 +84,7 @@ module RailsGuides
@warnings = ENV['WARNINGS'] == '1'
@all = ENV['ALL'] == '1'
@kindle = ENV['KINDLE'] == '1'
- @version = ENV['RAILS_VERSION'] || `git rev-parse --short HEAD`.chomp
+ @version = ENV['RAILS_VERSION'] || 'local'
@lang = ENV['GUIDES_LANGUAGE']
end
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index b4442130ac..42794b180e 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -1,42 +1,24 @@
Ruby on Rails 4.0 Release Notes
===============================
-Highlights in Rails 4.0: (WIP)
+Highlights in Rails 4.0:
* Ruby 1.9.3 only
* Strong Parameters
* Queue API
-* Caching Improvements
-* ActionController::Live
+* Turbolinks
+* Russian Doll Caching
+* Asynchronous Mailers
-These release notes cover the major changes, but do not include each bug-fix and changes. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub.
+These release notes cover only the major changes. To know about various bug fixes and changes, please refer to the change logs or check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub.
--------------------------------------------------------------------------------
Upgrading to Rails 4.0
----------------------
-TODO. This is a WIP guide.
+If you're upgrading an existing application, it's a great idea to have good test coverage before going in. You should also first upgrade to Rails 3.2 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 4.0. A list of things to watch out for when upgrading is available in the [Upgrading to Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-3-2-to-rails-4-0) guide.
-If you're upgrading an existing application, it's a great idea to have good test coverage before going in. You should also first upgrade to Rails 3.2 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 4.0. Then take heed of the following changes:
-
-### Rails 4.0 requires at least Ruby 1.9.3
-
-Rails 4.0 requires Ruby 1.9.3 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible.
-
-### What to update in your apps
-
-* Update your Gemfile to depend on
- * `rails = 4.0.0`
- * `sass-rails ~> 3.2.3`
- * `coffee-rails ~> 3.2.1`
- * `uglifier >= 1.0.3`
-
-TODO: Update the versions above.
-
-* Rails 4.0 removes `vendor/plugins` completely. You have to replace these plugins by extracting them as gems and adding them in your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`.
-
-TODO: Configuration changes in environment files
Creating a Rails 4.0 application
--------------------------------
@@ -70,11 +52,14 @@ $ ruby /path/to/rails/railties/bin/rails new myapp --dev
Major Features
--------------
-Moved to a Plugin
------------------
+TODO. Give a list and then talk about each of them briefly. We can point to relevant code commits or documentation from these sections.
+
+![Rails 4.0](images/rails4_features.png)
-With Rails 4 several pieces have been extracted. While Rails won't ship with these features anymore,
-you can simply add the extracted plugin to your `Gemfile` to bring the functionality back.
+Extraction of features to gems
+---------------------------
+
+In Rails 4.0, several features have been extracted into gems. You can simply add the extracted gems to your `Gemfile` to bring the functionality back.
* Hash-based & Dynamic finder methods ([Github](https://github.com/rails/activerecord-deprecated_finders))
* Mass assignment protection in Active Record models ([Github](https://github.com/rails/protected_attributes), [Pull Request](https://github.com/rails/rails/pull/7251))
@@ -83,6 +68,7 @@ you can simply add the extracted plugin to your `Gemfile` to bring the functiona
* Active Resource ([Github](https://github.com/rails/activeresource), [Pull Request](https://github.com/rails/rails/pull/572), [Blog](http://yetimedia.tumblr.com/post/35233051627/activeresource-is-dead-long-live-activeresource))
* Action Caching ([Github](https://github.com/rails/actionpack-action_caching), [Pull Request](https://github.com/rails/rails/pull/7833))
* Page Caching ([Github](https://github.com/rails/actionpack-page_caching), [Pull Request](https://github.com/rails/rails/pull/7833))
+* Sprockets ([Github](https://github.com/rails/sprockets-rails))
Documentation
-------------
@@ -94,848 +80,93 @@ Documentation
Railties
--------
-* Ensure that RAILS_ENV is set when accessing Rails.env.
-
-* Don't eager-load app/assets and app/views.
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes.
-* Add `.rake` to list of file extensions included by `rake notes` and `rake notes:custom`.
+### Notable changes
* New test locations `test/models`, `test/helpers`, `test/controllers`, and `test/mailers`. Corresponding rake tasks added as well. ([Pull Request](https://github.com/rails/rails/pull/7878))
-* Set a different cache per environment for assets pipeline through `config.assets.cache`.
-
-* `Rails.public_path` now returns a Pathname object.
-
-* Remove highly uncommon `config.assets.manifest` option for moving the manifest path. This option is now unsupported in sprockets-rails.
-
-* Add `config.action_controller.permit_all_parameters` to disable StrongParameters protection, it's false by default.
-
-* Remove `config.active_record.whitelist_attributes` and `config.active_record.mass_assignment_sanitizer` from new applications since MassAssignmentSecurity has been extracted from Rails.
-
-* Change `rails new` and `rails plugin new` generators to name the `.gitkeep` files as `.keep` in a more SCM-agnostic way. Change `--skip-git` option to only skip the `.gitignore` file and still generate the `.keep` files. Add `--skip-keeps` option to skip the `.keep` files.
-
-* Fixed support for DATABASE_URL environment variable for rake db tasks.
-
-* rails dbconsole now can use SSL for MySQL. The database.yml options sslca, sslcert, sslcapath, sslcipher and sslkey now affect rails dbconsole.
-
-* Correctly handle SCRIPT_NAME when generating routes to engine in application that's mounted at a sub-uri. With this behavior, you *should not* use default_url_options[:script_name] to set proper application's mount point by yourself.
-
-* `config.threadsafe!` is deprecated in favor of `config.eager_load` which provides a more fine grained control on what is eager loaded.
-
-* The migration generator will now produce AddXXXToYYY/RemoveXXXFromYYY migrations with references statements, for instance
-
- rails g migration AddReferencesToProducts user:references supplier:references{polymorphic}
-
- will generate the migration with:
-
- add_reference :products, :user, index: true
- add_reference :products, :supplier, polymorphic: true, index: true
-
-* Allow scaffold/model/migration generators to accept a `polymorphic` modifier for `references`/`belongs_to`, for instance
-
- ```
- rails g model Product supplier:references{polymorphic}
- ```
-
- will generate the model with `belongs_to :supplier, polymorphic: true` association and appropriate migration.
-
-* Set `config.active_record.migration_error` to `:page_load` for development.
-
-* Add runner to `Rails::Railtie` as a hook called just after runner starts.
-
-* Add `/rails/info/routes` path which displays the same information as `rake routes`.
-
-* Improved `rake routes` output for redirects.
-
-* Load all environments available in `config.paths["config/environments"]`.
-
-* Add `config.queue_consumer` to change the job queue consumer from the default `ActiveSupport::ThreadedQueueConsumer`.
-
-* Add `Rails.queue` for processing jobs in the background.
-
-* Remove `Rack::SSL` in favour of `ActionDispatch::SSL`.
-
-* Allow to set class that will be used to run as a console, other than IRB, with `Rails.application.config.console=`. It's best to add it to console block.
-
- ```ruby
- # it can be added to config/application.rb
- console do
- # this block is called only when running console,
- # so we can safely require pry here
- require "pry"
- config.console = Pry
- end
- ```
-
-* Add a convenience method `hide!` to Rails generators to hide the current generator namespace from showing when running `rails generate`.
+* Threadsafe on by default
-* Scaffold now uses `content_tag_for` in `index.html.erb`.
-
-* `Rails::Plugin` is removed. Instead of adding plugins to `vendor/plugins`, use gems or bundler with path or git dependencies.
+* Add `Rails.queue` for processing jobs in the background.
### Deprecations
-Action Mailer
--------------
-
-* Allow to set default Action Mailer options via `config.action_mailer.default_options=`.
-
-* Raise an `ActionView::MissingTemplate` exception when no implicit template could be found.
-
-* Asynchronously send messages via the Rails Queue. ([Pull Request](https://github.com/rails/rails/pull/6839))
-
-* Delivery Options (such as SMTP Settings) can now be set dynamically per mailer action.
-
- Delivery options are set via <tt>:delivery_method_options</tt> key on mail.
-
- ```ruby
- def welcome_mailer(user,company)
- delivery_options = { user_name: company.smtp_user, password: company.smtp_password, address: company.smtp_host }
- mail(to: user.email, subject: "Welcome!", delivery_method_options: delivery_options)
- end
- ```
-
-* Allow for callbacks in mailers similar to ActionController::Base. You can now set up headers/attachments using `before_filter` or `after_filter`. You could also change delivery settings or prevent delivery in an after filter based on instance variables set in your mailer action. You have access to `ActionMailer::Base` instance methods like `message`, `attachments`, `headers`.
-
-Action Pack
------------
-
-### Action Controller
-
-* Add `ActionController::Flash.add_flash_types` method to allow people to register their own flash types. e.g.:
-
- ```ruby
- class ApplicationController
- add_flash_types :error, :warning
- end
- ```
-
- If you add the above code, you can use `<%= error %>` in an erb, and `redirect_to /foo, :error => 'message'` in a controller.
-
-* Encrypted Cookies + Sign using Derived Keys. ([Pull Request](https://github.com/rails/rails/pull/8112))
-
-* Remove Active Model dependency from Action Pack.
-
-* Support unicode characters in routes. Route will be automatically escaped, so instead of manually escaping:
-
- ```ruby
- get Rack::Utils.escape('こんにちは') => 'home#index'
- ```
-
- You just have to write the unicode route:
-
- ```ruby
- get 'こんにちは' => 'home#index'
- ```
-
-* Return proper format on exceptions.
-
-* Extracted redirect logic from `ActionController::ForceSSL::ClassMethods.force_ssl` into `ActionController::ForceSSL#force_ssl_redirect`.
-
-* URL path parameters with invalid encoding now raise `ActionController::BadRequest`.
-
-* Malformed query and request parameter hashes now raise `ActionController::BadRequest`.
-
-* `respond_to` and `respond_with` now raise `ActionController::UnknownFormat` instead of directly returning head 406. The exception is rescued and converted to 406 in the exception handling middleware.
-
-* JSONP now uses `application/javascript` instead of `application/json` as the MIME type.
-
-* Session arguments passed to process calls in functional tests are now merged into the existing session, whereas previously they would replace the existing session. This change may break some existing tests if they are asserting the exact contents of the session but should not break existing tests that only assert individual keys.
-
-* Forms of persisted records use always PATCH (via the `_method` hack).
-
-* For resources, both PATCH and PUT are routed to the `update` action.
-
-* Don't ignore `force_ssl` in development. This is a change of behavior - use an `:if` condition to recreate the old behavior.
-
- ```ruby
- class AccountsController < ApplicationController
- force_ssl :if => :ssl_configured?
-
- def ssl_configured?
- !Rails.env.development?
- end
- end
- ```
-
-#### Deprecations
-
-* Deprecated `ActionController::Integration` in favour of `ActionDispatch::Integration`.
-
-* Deprecated `ActionController::IntegrationTest` in favour of `ActionDispatch::IntegrationTest`.
-
-* Deprecated `ActionController::PerformanceTest` in favour of `ActionDispatch::PerformanceTest`.
-
-* Deprecated `ActionController::AbstractRequest` in favour of `ActionDispatch::Request`.
-
-* Deprecated `ActionController::Request` in favour of `ActionDispatch::Request`.
-
-* Deprecated `ActionController::AbstractResponse` in favour of `ActionDispatch::Response`.
-
-* Deprecated `ActionController::Response` in favour of `ActionDispatch::Response`.
-
-* Deprecated `ActionController::Routing` in favour of `ActionDispatch::Routing`.
-
-### Action Dispatch
-
-* Add Routing Concerns to declare common routes that can be reused inside others resources and routes.
-
- Code before:
-
- ```ruby
- resources :messages do
- resources :comments
- end
-
- resources :posts do
- resources :comments
- resources :images, only: :index
- end
- ```
-
- Code after:
-
- ```ruby
- concern :commentable do
- resources :comments
- end
-
- concern :image_attachable do
- resources :images, only: :index
- end
-
- resources :messages, concerns: :commentable
-
- resources :posts, concerns: [:commentable, :image_attachable]
- ```
-
-* Show routes in exception page while debugging a `RoutingError` in development.
-
-* Helper methods for HTML5 inputs. ([Pull Request](https://github.com/rails/rails/pull/6359))
-
-* Include `mounted_helpers` (helpers for accessing mounted engines) in `ActionDispatch::IntegrationTest` by default.
-
-* Added `ActionDispatch::SSL` middleware that when included force all the requests to be under HTTPS protocol.
-
-* Copy literal route constraints to defaults so that url generation know about them. The copied constraints are `:protocol`, `:subdomain`, `:domain`, `:host` and `:port`.
-
-* Allows `assert_redirected_to` to match against a regular expression.
-
-* Adds a backtrace to the routing error page in development.
-
-* `assert_generates`, `assert_recognizes`, and `assert_routing` all raise `Assertion` instead of `RoutingError`.
-
-* Allows the route helper root to take a string argument. For example, `root 'pages#main'` as a shortcut for `root to: 'pages#main'`.
-
-* Adds support for the PATCH verb: Request objects respond to `patch?`. Routes now have a new `patch` method, and understand `:patch` in the existing places where a verb is configured, like `:via`. Functional tests have a new method `patch` and integration tests have a new method `patch_via_redirect`.
-If `:patch` is the default verb for updates, edits are tunneled as `PATCH` rather than as `PUT` and routing acts accordingly.
-
-* Integration tests support the OPTIONS method.
-
-* `expires_in` accepts a `must_revalidate` flag. If true, "must-revalidate" is added to the `Cache-Control` header.
-
-* Default responder will now always use your overridden block in `respond_with` to render your response.
-
-* Turn off verbose mode of `rack-cache`, we still have `X-Rack-Cache` to check that info.
-
-#### Deprecations
-
-### Action View
-
-* Remove Active Model dependency from Action Pack.
-
-* Allow to use `mounted_helpers` (helpers for accessing mounted engines) in `ActionView::TestCase`.
-
-* Make current object and counter (when it applies) variables accessible when rendering templates with `:object` or `:collection`.
-
-* Allow to lazy load `default_form_builder` by passing a string instead of a constant.
-
-* Add index method to `FormBuilder` class.
-
-* Adds support for layouts when rendering a partial with a given collection.
-
-* Remove `:disable_with` in favor of `data-disable-with` option from `submit_tag`, `button_tag` and `button_to` helpers.
-
-* Remove `:mouseover` option from `image_tag` helper.
-
-* Templates without a handler extension now raises a deprecation warning but still defaults to `ERb`. In future releases, it will simply return the template content.
-
-* Add a `divider` option to `grouped_options_for_select` to generate a separator optgroup automatically, and deprecate prompt as third argument, in favor of using an options hash.
-
-* Add `time_field` and `time_field_tag` helpers which render an `input[type="time"]` tag.
-
-* Removed old `text_helper` apis for `highlight`, `excerpt` and `word_wrap`.
-
-* Remove the leading \n added by textarea on `assert_select`.
-
-* Changed default value for `config.action_view.embed_authenticity_token_in_remote_forms` to false. This change breaks remote forms that need to work also without JavaScript, so if you need such behavior, you can either set it to true or explicitly pass `:authenticity_token => true` in form options.
-
-* Make possible to use a block in `button_to` helper if button text is hard to fit into the name parameter:
-
- ```ruby
- <%= button_to [:make_happy, @user] do %>
- Make happy <strong><%= @user.name %></strong>
- <% end %>
- # => "<form method="post" action="/users/1/make_happy" class="button_to">
- # <div>
- # <button type="submit">
- # Make happy <strong>Name</strong>
- # </button>
- # </div>
- # </form>"
- ```
-
-* Replace `include_seconds` boolean argument with `:include_seconds => true` option in `distance_of_time_in_words` and `time_ago_in_words` signature.
-
-* Remove `button_to_function` and `link_to_function` helpers.
-
-* `truncate` now always returns an escaped HTML-safe string. The option `:escape` can be used as `false` to not escape the result.
-
-* `truncate` now accepts a block to show extra content when the text is truncated.
-
-* Add `week_field`, `week_field_tag`, `month_field`, `month_field_tag`, `datetime_local_field`, `datetime_local_field_tag`, `datetime_field` and `datetime_field_tag` helpers.
-
-* Add `color_field` and `color_field_tag` helpers.
-
-* Add `include_hidden` option to select tag. With `:include_hidden => false` select with multiple attribute doesn't generate hidden input with blank value.
-
-* Removed default size option from the `text_field`, `search_field`, `telephone_field`, `url_field`, `email_field` helpers.
-
-* Removed default cols and rows options from the `text_area` helper.
-
-* Adds `image_url`, `javascript_url`, `stylesheet_url`, `audio_url`, `video_url`, and `font_url` to assets tag helper. These URL helpers will return the full path to your assets. This is useful when you are going to reference this asset from external host.
-
-* Allow `value_method` and `text_method` arguments from `collection_select` and `options_from_collection_for_select` to receive an object that responds to `:call` such as a proc, to evaluate the option in the current element context. This works the same way with `collection_radio_buttons` and `collection_check_boxes`.
-
-* Add `date_field` and `date_field_tag` helpers which render an `input[type="date"]` tag.
-
-* Add `collection_check_boxes` form helper, similar to `collection_select`:
-
- ```ruby
- collection_check_boxes :post, :author_ids, Author.all, :id, :name
- # Outputs something like:
- <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" />
- <label for="post_author_ids_1">D. Heinemeier Hansson</label>
- <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
- <label for="post_author_ids_2">D. Thomas</label>
- <input name="post[author_ids][]" type="hidden" value="" />
- ```
-
- The label/check_box pairs can be customized with a block.
-
-* Add `collection_radio_buttons` form helper, similar to `collection_select`:
-
- ```ruby
- collection_radio_buttons :post, :author_id, Author.all, :id, :name
- # Outputs something like:
- <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" />
- <label for="post_author_id_1">D. Heinemeier Hansson</label>
- <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
- <label for="post_author_id_2">D. Thomas</label>
- ```
-
- The label/radio_button pairs can be customized with a block.
-
-* `check_box` with an HTML5 attribute `:form` will now replicate the `:form` attribute to the hidden field as well.
-
-* label form helper accepts `:for => nil` to not generate the attribute.
-
-* Add `:format` option to `number_to_percentage`.
-
-* Add `config.action_view.logger` to configure logger for `Action View`.
+* `config.threadsafe!` is deprecated in favor of `config.eager_load` which provides a more fine grained control on what is eager loaded.
-* `check_box` helper with `:disabled => true` will generate a `disabled` hidden field to conform with the HTML convention where disabled fields are not submitted with the form. This is a behavior change, previously the hidden tag had a value of the disabled checkbox.
+* `Rails::Plugin` has gone. Instead of adding plugins to `vendor/plugins` use gems or bundler with path or git dependencies.
-* `favicon_link_tag` helper will now use the favicon in `app/assets` by default.
-
-* `ActionView::Helpers::TextHelper#highlight` now defaults to the HTML5 `mark` element.
-
-#### Deprecations
-
-### Sprockets
-
-Moved into a separate gem `sprockets-rails`.
-
-Active Record
+Action Mailer
-------------
-* Add `add_reference` and `remove_reference` schema statements. Aliases, `add_belongs_to` and `remove_belongs_to` are acceptable. References are reversible.
-
- ```ruby
- # Create a user_id column
- add_reference(:products, :user)
-
- # Create a supplier_id, supplier_type columns and appropriate index
- add_reference(:products, :supplier, polymorphic: true, index: true)
-
- # Remove polymorphic reference
- remove_reference(:products, :supplier, polymorphic: true)
- ```
-
-* Add `:default` and `:null` options to `column_exists?`.
-
- ```ruby
- column_exists?(:testings, :taggable_id, :integer, null: false)
- column_exists?(:testings, :taggable_type, :string, default: 'Photo')
- ```
-
-* `ActiveRecord::Relation#inspect` now makes it clear that you are dealing with a `Relation` object rather than an array:
-
- ```ruby
- User.where(:age => 30).inspect
- # => <ActiveRecord::Relation [#<User ...>, #<User ...>]>
-
- User.where(:age => 30).to_a.inspect
- # => [#<User ...>, #<User ...>]
- ```
-
- if more than 10 items are returned by the relation, inspect will only show the first 10 followed by ellipsis.
-
-* Add `:collation` and `:ctype` support to PostgreSQL. These are available for PostgreSQL 8.4 or later.
-
- ```yaml
- development:
- adapter: postgresql
- host: localhost
- database: rails_development
- username: foo
- password: bar
- encoding: UTF8
- collation: ja_JP.UTF8
- ctype: ja_JP.UTF8
- ```
-
-* `FinderMethods#exists?` now returns `false` with the `false` argument.
-
-* Added support for specifying the precision of a timestamp in the postgresql adapter. So, instead of having to incorrectly specify the precision using the `:limit` option, you may use `:precision`, as intended. For example, in a migration:
-
- ```ruby
- def change
- create_table :foobars do |t|
- t.timestamps :precision => 0
- end
- end
- ```
-
-* Allow `ActiveRecord::Relation#pluck` to accept multiple columns. Returns an array of arrays containing the typecasted values:
-
- ```ruby
- Person.pluck(:id, :name)
- # SELECT people.id, people.name FROM people
- # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
- ```
-
-* Improve the derivation of HABTM join table name to take account of nesting. It now takes the table names of the two models, sorts them lexically and then joins them, stripping any common prefix from the second table name. Some examples:
-
- ```
- Top level models (Category <=> Product)
- Old: categories_products
- New: categories_products
-
- Top level models with a global table_name_prefix (Category <=> Product)
- Old: site_categories_products
- New: site_categories_products
-
- Nested models in a module without a table_name_prefix method (Admin::Category <=> Admin::Product)
- Old: categories_products
- New: categories_products
-
- Nested models in a module with a table_name_prefix method (Admin::Category <=> Admin::Product)
- Old: categories_products
- New: admin_categories_products
-
- Nested models in a parent model (Catalog::Category <=> Catalog::Product)
- Old: categories_products
- New: catalog_categories_products
-
- Nested models in different parent models (Catalog::Category <=> Content::Page)
- Old: categories_pages
- New: catalog_categories_content_pages
- ```
-
-* Move HABTM validity checks to `ActiveRecord::Reflection`. One side effect of this is to move when the exceptions are raised from the point of declaration to when the association is built. This is consistant with other association validity checks.
-
-* Added `stored_attributes` hash which contains the attributes stored using `ActiveRecord::Store`. This allows you to retrieve the list of attributes you've defined.
-
- ```ruby
- class User < ActiveRecord::Base
- store :settings, accessors: [:color, :homepage]
- end
-
- User.stored_attributes[:settings] # [:color, :homepage]
- ```
-
-* PostgreSQL default log level is now 'warning', to bypass the noisy notice messages. You can change the log level using the `min_messages` option available in your `config/database.yml`.
-
-* Add uuid datatype support to PostgreSQL adapter.
-
-* Added `ActiveRecord::Migration.check_pending!` that raises an error if migrations are pending.
-
-* Added `#destroy!` which acts like `#destroy` but will raise an `ActiveRecord::RecordNotDestroyed` exception instead of returning `false`.
-
-* Allow blocks for count with `ActiveRecord::Relation`, to work similar as `Array#count`: `Person.where("age > 26").count { |person| person.gender == 'female' }`
-
-* Added support to `CollectionAssociation#delete` for passing fixnum or string values as record ids. This finds the records responding to the ids and deletes them.
-
- ```ruby
- class Person < ActiveRecord::Base
- has_many :pets
- end
-
- person.pets.delete("1") # => [#<Pet id: 1>]
- person.pets.delete(2, 3) # => [#<Pet id: 2>, #<Pet id: 3>]
- ```
-
-* It's not possible anymore to destroy a model marked as read only.
-
-* Added ability to `ActiveRecord::Relation#from` to accept other `ActiveRecord::Relation` objects.
-
-* Added custom coders support for `ActiveRecord::Store`. Now you can set your custom coder like this:
-
- ```ruby
- store :settings, accessors: [ :color, :homepage ], coder: JSON
- ```
-
-* `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by default to avoid silent data loss. This can be disabled by specifying `strict: false` in `config/database.yml`. ([Pull Request](https://github.com/rails/rails/pull/6069))
-
-* Added default order to `ActiveRecord::Base#first` to assure consistent results among different database engines. Introduced `ActiveRecord::Base#take` as a replacement to the old behavior.
-
-* Added an `:index` option to automatically create indexes for `references` and `belongs_to` statements in migrations. This can be either a boolean or a hash that is identical to options available to the `add_index` method:
-
- ```ruby
- create_table :messages do |t|
- t.references :person, :index => true
- end
- ```
-
- Is the same as:
-
- ```ruby
- create_table :messages do |t|
- t.references :person
- end
- add_index :messages, :person_id
- ```
-
- Generators have also been updated to use the new syntax.
-
-* Added bang methods for mutating `ActiveRecord::Relation` objects. For example, while `foo.where(:bar)` will return a new object leaving foo unchanged, `foo.where!(:bar)` will mutate the foo object.
-
-* Added `#find_by` and `#find_by!` to mirror the functionality provided by dynamic finders in a way that allows dynamic input more easily:
-
- ```ruby
- Post.find_by name: 'Spartacus', rating: 4
- Post.find_by "published_at < ?", 2.weeks.ago
- Post.find_by! name: 'Spartacus'
- ```
-
-* Added `ActiveRecord::Base#slice` to return a hash of the given methods with their names as keys and returned values as values.
-
-* Remove IdentityMap - IdentityMap has never graduated to be an "enabled-by-default" feature, due to some inconsistencies with associations, as described in this [commit](https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6). Hence the removal from the codebase, until such issues are fixed.
-
-* Added a feature to dump/load internal state of `SchemaCache` instance because we want to boot more quickly when we have many models. ([Pull Request](https://github.com/rails/rails/pull/5162))
-
- ```ruby
- # execute rake task.
- RAILS_ENV=production bundle exec rake db:schema:cache:dump
- => generate db/schema_cache.dump
-
- # add config.use_schema_cache_dump = true in config/production.rb. BTW, true is default.
-
- # boot rails.
- RAILS_ENV=production bundle exec rails server
- => use db/schema_cache.dump
-
- # If you remove clear dumped cache, execute rake task.
- RAILS_ENV=production bundle exec rake db:schema:cache:clear
- => remove db/schema_cache.dump
- ```
-
-* Added support for partial indices to `PostgreSQL` adapter.
-
-* The `add_index` method now supports a `where` option that receives a string with the partial index criteria.
-
-* Added the `ActiveRecord::NullRelation` class implementing the null object pattern for the Relation class.
-
-* Implemented `ActiveRecord::Relation#none` method which returns a chainable relation with zero records (an instance of the `NullRelation` class). Any subsequent condition chained to the returned relation will continue generating an empty relation and will not fire any query to the database.
-
-* Added `create_join_table` migration helper to create HABTM join tables.
-
- ```ruby
- create_join_table :products, :categories
- # =>
- # create_table :categories_products, :id => false do |td|
- # td.integer :product_id, :null => false
- # td.integer :category_id, :null => false
- # end
- ```
-
-* The primary key is always initialized in the `@attributes` hash to nil (unless another value has been specified).
-
-* In previous releases, the following would generate a single query with an OUTER JOIN comments, rather than two separate queries:
-
- ```ruby
- Post.includes(:comments).where("comments.name = 'foo'")
- ```
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/actionmailer/CHANGELOG.md) for detailed changes.
- This behaviour relies on matching SQL string, which is an inherently flawed idea unless we write an SQL parser, which we do not wish to do. Therefore, it is now deprecated.
+### Notable changes
- To avoid deprecation warnings and for future compatibility, you must explicitly state which tables you reference, when using SQL snippets:
-
- ```ruby
- Post.includes(:comments).where("comments.name = 'foo'").references(:comments)
- ```
-
- Note that you do not need to explicitly specify references in the following cases, as they can be automatically inferred:
-
- ```ruby
- Post.where(comments: { name: 'foo' })
- Post.where('comments.name' => 'foo')
- Post.order('comments.name')
- ```
-
- You also do not need to worry about this unless you are doing eager loading. Basically, don't worry unless you see a deprecation warning or (in future releases) an SQL error due to a missing JOIN.
-
-* Support for the `schema_info` table has been dropped. Please switch to `schema_migrations`.
-
-* Connections *must* be closed at the end of a thread. If not, your connection pool can fill and an exception will be raised.
-
-* PostgreSQL hstore records can be created.
-
-* PostgreSQL hstore types are automatically deserialized from the database.
-
-* Support for array datatype in PostgreSQL. ([Pull Request](https://github.com/rails/rails/pull/7547))
-
-* Added `#update_columns` method which updates the attributes from the passed-in hash without calling save, hence skipping validations and callbacks. `ActiveRecordError` will be raised when called on new objects or when at least one of the attributes is marked as read only.
-
- ```ruby
- post.attributes # => {"id"=>2, "title"=>"My title", "body"=>"My content", "author"=>"Peter"}
- post.update_columns({title: 'New title', author: 'Sebastian'}) # => true
- post.attributes # => {"id"=>2, "title"=>"New title", "body"=>"My content", "author"=>"Sebastian"}
- ```
+* Asynchronously send messages via the Rails Queue. ([Pull Request](https://github.com/rails/rails/pull/6839))
### Deprecations
-* Deprecated most of the 'dynamic finder' methods. All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's how you can rewrite the code:
-
- ```ruby
- find_all_by_... can be rewritten using where(...)
- find_last_by_... can be rewritten using where(...).last
- scoped_by_... can be rewritten using where(...)
- find_or_initialize_by_... can be rewritten using where(...).first_or_initialize
- find_or_create_by_... can be rewritten using where(...).first_or_create
- find_or_create_by_...! can be rewritten using where(...).first_or_create!
- ```
-
- The implementation of the deprecated dynamic finders has been moved to the `active_record_deprecated_finders` gem.
-
-* Deprecated the old-style hash based finder API. This means that methods which previously accepted "finder options" no longer do. For example this:
-
- ```ruby
- Post.find(:all, :conditions => { :comments_count => 10 }, :limit => 5)
- ```
-
- should be rewritten in the new style which has existed since Rails 3:
-
- ```ruby
- Post.where(comments_count: 10).limit(5)
- ```
-
- Note that as an interim step, it is possible to rewrite the above as:
-
- ```ruby
- Post.scoped(:where => { :comments_count => 10 }, :limit => 5)
- ```
-
- This could save you a lot of work if there is a lot of old-style finder usage in your application.
-
- Calling `Post.scoped(options)` is a shortcut for `Post.scoped.merge(options)`. `Relation#merge` now accepts a hash of options, but they must be identical to the names of the equivalent finder method. These are mostly identical to the old-style finder option names, except in the following cases:
-
- ```
- :conditions becomes :where
- :include becomes :includes
- :extend becomes :extending
- ```
-
- The code to implement the deprecated features has been moved out to the `active_record_deprecated_finders` gem. This gem is a dependency of Active Record in Rails 4.0. It will no longer be a dependency from Rails 4.1, but if your app relies on the deprecated features then you can add it to your own Gemfile. It will be maintained by the Rails core team until Rails 5.0 is released.
-
-* Deprecate eager-evaluated scopes.
-
- Don't use this:
-
- ```ruby
- scope :red, where(color: 'red')
- default_scope where(color: 'red')
- ```
-
- Use this:
-
- ```ruby
- scope :red, -> { where(color: 'red') }
- default_scope { where(color: 'red') }
- ```
-
- The former has numerous issues. It is a common newbie gotcha to do the following:
-
- ```ruby
- scope :recent, where(published_at: Time.now - 2.weeks)
- ```
-
- Or a more subtle variant:
-
- ```ruby
- scope :recent, -> { where(published_at: Time.now - 2.weeks) }
- scope :recent_red, recent.where(color: 'red')
- ```
-
- Eager scopes are also very complex to implement within Active Record, and there are still bugs. For example, the following does not do what you expect:
-
- ```ruby
- scope :remove_conditions, except(:where)
- where(...).remove_conditions # => still has conditions
- ```
-
-* Added deprecation for the `:dependent => :restrict` association option.
-
-* Up until now `has_many` and `has_one, :dependent => :restrict` option raised a `DeleteRestrictionError` at the time of destroying the object. Instead, it will add an error on the model.
-
-* To fix this warning, make sure your code isn't relying on a `DeleteRestrictionError` and then add `config.active_record.dependent_restrict_raises = false` to your application config.
-
-* New rails application would be generated with the `config.active_record.dependent_restrict_raises = false` in the application config.
-
-* The migration generator now creates a join table with (commented) indexes every time the migration name contains the word "join_table".
-
-* `ActiveRecord::SessionStore` is removed from Rails 4.0 and is now a separate [gem](https://github.com/rails/activerecord-session_store).
-
Active Model
------------
-* Changed `AM::Serializers::JSON.include_root_in_json` default value to false. Now, AM Serializers and AR objects have the same default behaviour.
-
- ```ruby
- class User < ActiveRecord::Base; end
-
- class Person
- include ActiveModel::Model
- include ActiveModel::AttributeMethods
- include ActiveModel::Serializers::JSON
-
- attr_accessor :name, :age
-
- def attributes
- instance_values
- end
- end
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activemodel/CHANGELOG.md) for detailed changes.
- user.as_json
- => {"id"=>1, "name"=>"Konata Izumi", "age"=>16, "awesome"=>true}
- # root is not included
+### Notable changes
- person.as_json
- => {"name"=>"Francesco", "age"=>22}
- # root is not included
- ```
+* Add `ActiveModel::ForbiddenAttributesProtection`, a simple module to protect attributes from mass assignment when non-permitted attributes are passed.
-* Passing false hash values to `validates` will no longer enable the corresponding validators.
-
-* `ConfirmationValidator` error messages will attach to `:#{attribute}_confirmation` instead of `attribute`.
-
-* Added `ActiveModel::Model`, a mixin to make Ruby objects work with Action Pack out of the box. ([Pull Request](https://github.com/rails/rails/pull/5253))
-
-* `ActiveModel::Errors#to_json` supports a new parameter `:full_messages`.
-
-* Trims down the API by removing `valid?` and `errors.full_messages`.
+* Added `ActiveModel::Model`, a mixin to make Ruby objects work with AP out of box.
### Deprecations
-Active Resource
----------------
-
-* Active Resource is removed from Rails 4.0 and is now a separate [gem](https://github.com/rails/activeresource).
-
Active Support
--------------
-* Add default values to all `ActiveSupport::NumberHelper` methods, to avoid errors with empty locales or missing values.
-
-* `Time#change` now works with time values with offsets other than UTC or the local time zone.
-
-* Add `Time#prev_quarter` and `Time#next_quarter` short-hands for `months_ago(3)` and `months_since(3)`.
-
-* Add `Time#last_week`, `Time#last_month`, `Time#last_year` as aliases for `Time#prev_week`, `Time#prev_month`, and `Time#prev_year`.
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activesupport/CHANGELOG.md) for detailed changes.
-* Add `Date#last_week`, `Date#last_month`, `Date#last_year` as aliases for `Date#prev_week`, `Date#prev_month`, and `Date#prev_year`.
+### Notable changes
-* Remove obsolete and unused `require_association` method from dependencies.
+* Replace deprecated `memcache-client` gem with `dalli` in ActiveSupport::Cache::MemCacheStore.
-* Add `:instance_accessor` option for `config_accessor`.
+* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead.
- ```ruby
- class User
- include ActiveSupport::Configurable
- config_accessor :allowed_access, instance_accessor: false
- end
+* Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument.
- User.new.allowed_access = true # => NoMethodError
- User.new.allowed_access # => NoMethodError
- ```
+* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!`.
-* `ActionView::Helpers::NumberHelper` methods have been moved to `ActiveSupport::NumberHelper` and are now available via `Numeric#to_s`.
-
-* `Numeric#to_s` now accepts the formatting options :phone, :currency, :percentage, :delimited, :rounded, :human, and :human_size.
-
-* Add `Hash#transform_keys`, `Hash#transform_keys!`, `Hash#deep_transform_keys` and `Hash#deep_transform_keys!`.
-
-* Changed xml type datetime to dateTime (with upper case letter T).
-
-* Add `:instance_accessor` option for `class_attribute`.
-
-* `constantize` now looks in the ancestor chain.
-
-* Add `Hash#deep_stringify_keys` and `Hash#deep_stringify_keys!` to convert all keys from a `Hash` instance into strings.
-
-* Add `Hash#deep_symbolize_keys` and `Hash#deep_symbolize_keys!` to convert all keys from a `Hash` instance into symbols.
-
-* `Object#try` can't call private methods.
-
-* AS::Callbacks#run_callbacks remove key argument.
+### Deprecations
-* `deep_dup` works more expectedly now and duplicates also values in `Hash` instances and elements in `Array` instances.
+* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead.
-* Inflector no longer applies ice -> ouse to words like slice, police.
+* ActiveSupport::Benchmarkable#silence has been deprecated due to its lack of thread safety. It will be removed without replacement in Rails 4.1.
-* Add `ActiveSupport::Deprecations.behavior = :silence` to completely ignore Rails runtime deprecations.
+* `ActiveSupport::JSON::Variable` is deprecated. Define your own `#as_json` and `#encode_json` methods for custom JSON string literals.
-* Make `Module#delegate` stop using send - can no longer delegate to private methods.
+* Deprecates the compatibility method Module#local_constant_names, use Module#local_constants instead (which returns symbols).
-* AS::Callbacks deprecate :rescuable option.
+* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger from Ruby stdlib.
-* Adds `Integer#ordinal` to get the ordinal suffix string of an integer.
-* AS::Callbacks :per_key option is no longer supported.
+Action Pack
+-----------
-* AS::Callbacks#define_callbacks add :skip_after_callbacks_if_terminated option.
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes.
-* Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it.
+### Notable changes
-* Remove `ActiveSupport::TestCase#pending` method, use `skip` instead.
+### Deprecations
-* Deletes the compatibility method `Module#method_names`, use `Module#methods` from now on (which returns symbols).
-* Deletes the compatibility method `Module#instance_method_names`, use `Module#instance_methods` from now on (which returns symbols).
+Active Record
+-------------
-* Unicode database updated to 6.1.0.
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes.
-* Adds `encode_big_decimal_as_string` option to force JSON serialization of BigDecimals as numeric instead of wrapping them in strings for safety.
+### Notable changes
### Deprecations
-* `ActiveSupport::Callbacks`: deprecate usage of filter object with `#before` and `#after` methods as `around` callback.
-
-* `BufferedLogger` is deprecated. Use `ActiveSupport::Logger` or the `logger` from Ruby stdlib.
-
-* Deprecates the compatibility method `Module#local_constant_names` and use `Module#local_constants` instead (which returns symbols).
-
Credits
-------
diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb
index 9d2e9c1d68..a50961a0c7 100644
--- a/guides/source/_welcome.html.erb
+++ b/guides/source/_welcome.html.erb
@@ -1,4 +1,4 @@
-<h2>Ruby on Rails Guides (<%= @version %>)</h2>
+<h2>Ruby on Rails Guides (<%= @edge ? @version[0, 7] : @version %>)</h2>
<% if @edge %>
<p>
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 69d99becb9..63702e0b92 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -434,7 +434,7 @@ Filters are inherited, so if you set a filter on `ApplicationController`, it wil
```ruby
class ApplicationController < ActionController::Base
- before_filter :require_login
+ before_action :require_login
private
@@ -458,11 +458,11 @@ end
The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled.
-In this example the filter is added to `ApplicationController` and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with `skip_before_filter`:
+In this example the filter is added to `ApplicationController` and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with `skip_before_action`:
```ruby
class LoginsController < ApplicationController
- skip_before_filter :require_login, only: [:new, :create]
+ skip_before_action :require_login, only: [:new, :create]
end
```
@@ -480,7 +480,7 @@ For example, in a website where changes have an approval workflow an administrat
```ruby
class ChangesController < ActionController::Base
- around_filter :wrap_in_transaction, only: :show
+ around_action :wrap_in_transaction, only: :show
private
@@ -502,13 +502,13 @@ You can choose not to yield and build the response yourself, in which case the a
### Other Ways to Use Filters
-While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing.
+While the most common way to use filters is by creating private methods and using *_action to add them, there are two other ways to do the same thing.
-The first is to use a block directly with the *_filter methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block:
+The first is to use a block directly with the *_action methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block:
```ruby
class ApplicationController < ActionController::Base
- before_filter do |controller|
+ before_action do |controller|
redirect_to new_login_url unless controller.send(:logged_in?)
end
end
@@ -520,7 +520,7 @@ The second way is to use a class (actually, any object that responds to the righ
```ruby
class ApplicationController < ActionController::Base
- before_filter LoginFilter
+ before_action LoginFilter
end
class LoginFilter
@@ -648,7 +648,7 @@ HTTP digest authentication is superior to the basic authentication as it does no
class AdminController < ApplicationController
USERS = { "lifo" => "world" }
- before_filter :authenticate
+ before_action :authenticate
private
@@ -751,15 +751,36 @@ Now the user can request to get a PDF version of a client just by adding ".pdf"
GET /clients/1.pdf
```
-Parameter Filtering
--------------------
+Log Filtering
+-------------
+
+Rails keeps a log file for each environment in the `log` folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file.
-Rails keeps a log file for each environment in the `log` folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. You can filter certain request parameters from your log files by appending them to `config.filter_parameters` in the application configuration. These parameters will be marked [FILTERED] in the log.
+### Parameters Filtering
+
+You can filter certain request parameters from your log files by appending them to `config.filter_parameters` in the application configuration. These parameters will be marked [FILTERED] in the log.
```ruby
config.filter_parameters << :password
```
+### Redirects Filtering
+
+Sometimes it's desirable to filter out from log files some sensible locations your application is redirecting to.
+You can do that by using the `config.filter_redirect` configuration option:
+
+```ruby
+config.filter_redirect << 's3.amazonaws.com'
+```
+
+You can set it to a String, a Regexp, or an array of both.
+
+```ruby
+config.filter_redirect.concat ['s3.amazonaws.com', /private_path/]
+```
+
+Matching URLs will be marked as '[FILTERED]'.
+
Rescue
------
@@ -807,7 +828,7 @@ end
class ClientsController < ApplicationController
# Check that the user has the right authorization to access clients.
- before_filter :check_authorization
+ before_action :check_authorization
# Note how the actions don't have to worry about all the auth stuff.
def edit
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index ddb0e438c9..bf7692e2a2 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -447,17 +447,17 @@ end
Action Mailer Callbacks
---------------------------
-Action Mailer allows for you to specify a `before_filter`, `after_filter` and 'around_filter'.
+Action Mailer allows for you to specify a `before_action`, `after_action` and 'around_action'.
* Filters can be specified with a block or a symbol to a method in the mailer class similar to controllers.
-* You could use a `before_filter` to prepopulate the mail object with defaults, delivery_method_options or insert default headers and attachments.
+* You could use a `before_action` to prepopulate the mail object with defaults, delivery_method_options or insert default headers and attachments.
-* You could use an `after_filter` to do similar setup as a `before_filter` but using instance variables set in your mailer action.
+* You could use an `after_action` to do similar setup as a `before_action` but using instance variables set in your mailer action.
```ruby
class UserMailer < ActionMailer::Base
- after_filter :set_delivery_options, :prevent_delivery_to_guests, :set_business_headers
+ after_action :set_delivery_options, :prevent_delivery_to_guests, :set_business_headers
def feedback_message(business, user)
@business = business
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index c931b30bd3..47e8ba3a73 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1486,7 +1486,7 @@ You can use the same technique to localize the rescue files in your public direc
Since Rails doesn't restrict the symbols that you use to set I18n.locale, you can leverage this system to display different content depending on anything you like. For example, suppose you have some "expert" users that should see different pages from "normal" users. You could add the following to `app/controllers/application.rb`:
```ruby
-before_filter :set_expert_locale
+before_action :set_expert_locale
def set_expert_locale
I18n.locale = :expert if current_user.expert?
diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md
index 0c278095ab..68ac26c681 100644
--- a/guides/source/active_model_basics.md
+++ b/guides/source/active_model_basics.md
@@ -10,7 +10,7 @@ After reading this guide, you will know:
Introduction
------------
-Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below.
+Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below.
### AttributeMethods
@@ -26,23 +26,21 @@ class Person
attr_accessor :age
-private
- def reset_attribute(attribute)
- send("#{attribute}=", 0)
- end
+ private
+ def reset_attribute(attribute)
+ send("#{attribute}=", 0)
+ end
- def attribute_highest?(attribute)
- send(attribute) > 100 ? true : false
- end
-
+ def attribute_highest?(attribute)
+ send(attribute) > 100
+ end
end
person = Person.new
person.age = 110
person.age_highest? # true
person.reset_age # 0
-person.age_highest? # false
-
+person.age_highest? # false
```
### Callbacks
@@ -87,14 +85,14 @@ class Person
end
person = Person.new
-person.to_model == person #=> true
-person.to_key #=> nil
-person.to_param #=> nil
+person.to_model == person # => true
+person.to_key # => nil
+person.to_param # => nil
```
### Dirty
-An object becomes dirty when it has gone through one or more changes to its attributes and has not been saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Let's consider a Person class with attributes first_name and last_name
+An object becomes dirty when it has gone through one or more changes to its attributes and has not been saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Let's consider a Person class with attributes `first_name` and `last_name`:
```ruby
require 'active_model'
@@ -123,8 +121,8 @@ class Person
def save
@previously_changed = changes
+ # do save work...
end
-
end
```
@@ -132,21 +130,22 @@ end
```ruby
person = Person.new
-person.first_name = "First Name"
+person.changed? # => false
-person.first_name #=> "First Name"
-person.first_name = "First Name Changed"
+person.first_name = "First Name"
+person.first_name # => "First Name"
-person.changed? #=> true
+# returns if any attribute has changed.
+person.changed? # => true
-#returns an list of fields arry which all has been changed before saved.
-person.changed #=> ["first_name"]
+# returns a list of attributes that have changed before saving.
+person.changed # => ["first_name"]
-#returns a hash of the fields that have changed with their original values.
-person.changed_attributes #=> {"first_name" => "First Name Changed"}
+# returns a hash of the attributes that have changed with their original values.
+person.changed_attributes # => {"first_name"=>nil}
-#returns a hash of changes, with the attribute names as the keys, and the values will be an array of the old and new value for that field.
-person.changes #=> {"first_name" => ["First Name","First Name Changed"]}
+# returns a hash of changes, with the attribute names as the keys, and the values will be an array of the old and new value for that field.
+person.changes # => {"first_name"=>[nil, "First Name"]}
```
#### Attribute based accessor methods
@@ -154,28 +153,24 @@ person.changes #=> {"first_name" => ["First Name","First Name Changed"]}
Track whether the particular attribute has been changed or not.
```ruby
-#attr_name_changed?
-person.first_name #=> "First Name"
-
-#assign some other value to first_name attribute
-person.first_name = "First Name 1"
-
-person.first_name_changed? #=> true
+# attr_name_changed?
+person.first_name # => "First Name"
+person.first_name_changed? # => true
```
Track what was the previous value of the attribute.
```ruby
-#attr_name_was accessor
-person.first_name_was #=> "First Name"
+# attr_name_was accessor
+person.first_name_was # => "First Name"
```
Track both previous and current value of the changed attribute. Returns an array if changed, else returns nil.
```ruby
-#attr_name_change
-person.first_name_change #=> ["First Name", "First Name 1"]
-person.last_name_change #=> nil
+# attr_name_change
+person.first_name_change # => [nil, "First Name"]
+person.last_name_change # => nil
```
### Validations
@@ -187,20 +182,19 @@ class Person
include ActiveModel::Validations
attr_accessor :name, :email, :token
-
+
validates :name, presence: true
- validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
+ validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
validates! :token, presence: true
-
end
person = Person.new(token: "2b1f325")
-person.valid? #=> false
-person.name = 'vishnu'
-person.email = 'me'
-person.valid? #=> false
+person.valid? # => false
+person.name = 'vishnu'
+person.email = 'me'
+person.valid? # => false
person.email = 'me@vishnuatrai.com'
-person.valid? #=> true
+person.valid? # => true
person.token = nil
-person.valid? #=> raises ActiveModel::StrictValidationFailed
+person.valid? # => raises ActiveModel::StrictValidationFailed
```
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index cb64cf39f3..68c6416e89 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -147,15 +147,15 @@ Active Record objects can be created from a hash, a block or have their attribut
For example, given a model `User` with attributes of `name` and `occupation`, the `create` method call will create and save a new record into the database:
```ruby
- user = User.create(name: "David", occupation: "Code Artist")
+user = User.create(name: "David", occupation: "Code Artist")
```
Using the `new` method, an object can be created without being saved:
```ruby
- user = User.new
- user.name = "David"
- user.occupation = "Code Artist"
+user = User.new
+user.name = "David"
+user.occupation = "Code Artist"
```
A call to `user.save` will commit the record to the database.
@@ -163,10 +163,10 @@ A call to `user.save` will commit the record to the database.
Finally, if a block is provided, both `create` and `new` will yield the new object to that block for initialization:
```ruby
- user = User.new do |u|
- u.name = "David"
- u.occupation = "Code Artist"
- end
+user = User.new do |u|
+ u.name = "David"
+ u.occupation = "Code Artist"
+end
```
### Read
@@ -174,23 +174,23 @@ Finally, if a block is provided, both `create` and `new` will yield the new obje
Active Record provides a rich API for accessing data within a database. Below are a few examples of different data access methods provided by Active Record.
```ruby
- # return array with all records
- users = User.all
+# return array with all records
+users = User.all
```
```ruby
- # return the first record
- user = User.first
+# return the first record
+user = User.first
```
```ruby
- # return the first user named David
- david = User.find_by_name('David')
+# return the first user named David
+david = User.find_by_name('David')
```
```ruby
- # find all users named David who are Code Artists and sort by created_at in reverse chronological order
- users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')
+# find all users named David who are Code Artists and sort by created_at in reverse chronological order
+users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')
```
You can learn more about querying an Active Record model in the [Active Record Query Interface](active_record_querying.html) guide.
@@ -200,9 +200,9 @@ You can learn more about querying an Active Record model in the [Active Record Q
Once an Active Record object has been retrieved, its attributes can be modified and it can be saved to the database.
```ruby
- user = User.find_by_name('David')
- user.name = 'Dave'
- user.save
+user = User.find_by_name('David')
+user.name = 'Dave'
+user.save
```
### Delete
@@ -210,8 +210,8 @@ Once an Active Record object has been retrieved, its attributes can be modified
Likewise, once retrieved an Active Record object can be destroyed which removes it from the database.
```ruby
- user = User.find_by_name('David')
- user.destroy
+user = User.find_by_name('David')
+user.destroy
```
Validations
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index c45f3f2e6a..02c1c46a5a 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -200,7 +200,7 @@ Halting Execution
As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed.
-The whole callback chain is wrapped in a transaction. If any <em>before</em> callback method returns exactly `false` or raises an exception, the execution chain gets halted and a ROLLBACK is issued; <em>after</em> callbacks can only accomplish that by raising an exception.
+The whole callback chain is wrapped in a transaction. If any _before_ callback method returns exactly `false` or raises an exception, the execution chain gets halted and a ROLLBACK is issued; _after_ callbacks can only accomplish that by raising an exception.
WARNING. Raising an arbitrary exception may break code that expects `save` and its friends not to fail like that. The `ActiveRecord::Rollback` exception is thought precisely to tell Active Record a rollback is going on. That one is internally captured but not reraised.
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 9620270141..c942ffe267 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -505,6 +505,20 @@ This code will generate SQL like this:
SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
```
+### NOT, LIKE, and NOT LIKE Conditions
+
+`NOT`, `LIKE`, and `NOT LIKE` SQL queries can be built by `where.not`, `where.like`, and `where.not_like` respectively.
+
+```ruby
+Post.where.not(author: author)
+
+Author.where.like(name: 'Nari%')
+
+Developer.where.not_like(name: 'Tenderl%')
+```
+
+In other words, these sort of queries can be generated by calling `where` with no argument, then immediately chain with `not`, `like`, or `not_like` passing `where` conditions.
+
Ordering
--------
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index e752c6f3f9..4642ef82f0 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -264,7 +264,7 @@ class Person < ActiveRecord::Base
end
```
-The default error message for this helper is "_must be accepted_".
+The default error message for this helper is _"must be accepted"_.
It can receive an `:accept` option, which determines the value that will be
considered acceptance. It defaults to "1" and can be easily changed.
@@ -293,7 +293,7 @@ This validation will work with all of the association types.
CAUTION: Don't use `validates_associated` on both ends of your associations.
They would call each other in an infinite loop.
-The default error message for `validates_associated` is "_is invalid_". Note
+The default error message for `validates_associated` is _"is invalid"_. Note
that each associated object will contain its own `errors` collection; errors do
not bubble up to the calling model.
@@ -328,7 +328,7 @@ class Person < ActiveRecord::Base
end
```
-The default error message for this helper is "_doesn't match confirmation_".
+The default error message for this helper is _"doesn't match confirmation"_.
### `exclusion`
@@ -348,7 +348,7 @@ alias called `:within` that you can use for the same purpose, if you'd like to.
This example uses the `:message` option to show how you can include the
attribute's value.
-The default error message is "_is reserved_".
+The default error message is _"is reserved"_.
### `format`
@@ -362,7 +362,7 @@ class Product < ActiveRecord::Base
end
```
-The default error message is "_is invalid_".
+The default error message is _"is invalid"_.
### `inclusion`
@@ -381,7 +381,7 @@ will be accepted. The `:in` option has an alias called `:within` that you can
use for the same purpose, if you'd like to. The previous example uses the
`:message` option to show how you can include the attribute's value.
-The default error message for this helper is "_is not included in the list_".
+The default error message for this helper is _"is not included in the list"_.
### `length`
@@ -471,24 +471,24 @@ Besides `:only_integer`, this helper also accepts the following options to add
constraints to acceptable values:
* `:greater_than` - Specifies the value must be greater than the supplied
- value. The default error message for this option is "_must be greater than
- %{count}_".
+ value. The default error message for this option is _"must be greater than
+ %{count}"_.
* `:greater_than_or_equal_to` - Specifies the value must be greater than or
equal to the supplied value. The default error message for this option is
- "_must be greater than or equal to %{count}_".
+ _"must be greater than or equal to %{count}"_.
* `:equal_to` - Specifies the value must be equal to the supplied value. The
- default error message for this option is "_must be equal to %{count}_".
+ default error message for this option is _"must be equal to %{count}"_.
* `:less_than` - Specifies the value must be less than the supplied value. The
- default error message for this option is "_must be less than %{count}_".
+ default error message for this option is _"must be less than %{count}"_.
* `:less_than_or_equal_to` - Specifies the value must be less than or equal the
- supplied value. The default error message for this option is "_must be less
- than or equal to %{count}_".
+ supplied value. The default error message for this option is _"must be less
+ than or equal to %{count}"_.
* `:odd` - Specifies the value must be an odd number if set to true. The
- default error message for this option is "_must be odd_".
+ default error message for this option is _"must be odd"_.
* `:even` - Specifies the value must be an even number if set to true. The
- default error message for this option is "_must be even_".
+ default error message for this option is _"must be even"_.
-The default error message is "_is not a number_".
+The default error message is _"is not a number"_.
### `presence`
@@ -528,7 +528,7 @@ If you validate the presence of an object associated via a `has_one` or
Since `false.blank?` is true, if you want to validate the presence of a boolean
field you should use `validates :field_name, inclusion: { in: [true, false] }`.
-The default error message is "_can't be empty_".
+The default error message is _"can't be empty"_.
### `uniqueness`
@@ -570,7 +570,7 @@ end
WARNING. Note that some databases are configured to perform case-insensitive
searches anyway.
-The default error message is "_has already been taken_".
+The default error message is _"has already been taken"_.
### `validates_with`
@@ -714,7 +714,7 @@ class Person < ActiveRecord::Base
validates :name, presence: { strict: true }
end
-Person.new.valid? #=> ActiveModel::StrictValidationFailed: Name can't be blank
+Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
```
There is also an ability to pass custom exception to `:strict` option
@@ -724,7 +724,7 @@ class Person < ActiveRecord::Base
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
-Person.new.valid? #=> TokenGenerationException: Token can't be blank
+Person.new.valid? # => TokenGenerationException: Token can't be blank
```
Conditional Validation
@@ -917,7 +917,7 @@ validations fail.
Because every application handles this kind of thing differently, Rails does
not include any view helpers to help you generate these messages directly.
-However, due to the rich number of methods Rails gives you to interact with
+However, due to the rich number of methods Rails gives you to interact with
validations in general, it's fairly easy to build your own. In addition, when
generating a scaffold, Rails will put some ERB into the `_form.html.erb` that
it generates that displays the full list of errors on that model.
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index e737dcab83..773102400a 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -104,7 +104,7 @@ Let's say you only wanted authenticated users to call actions on `ProductsContro
```ruby
class ProductsController < ActionController
- before_filter :authenticate
+ before_action :authenticate
caches_action :index
def index
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index ceba2c65aa..b1ca8da292 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -520,7 +520,7 @@ invoking the command: `rake db:migrate RAILS_ENV=production`.
### Saving data in the controller
Back in `posts_controller`, we need to change the `create` action
-to use the new `Post` model to save the data in the database. Open that file
+to use the new `Post` model to save the data in the database. Open `app/controllers/posts_controller.rb`
and change the `create` action to look like this:
```ruby
@@ -558,8 +558,8 @@ parameter, which in our case will be the id of the post. Note that this
time we had to specify the actual mapping, `posts#show` because
otherwise Rails would not know which action to render.
-As we did before, we need to add the `show` action in the
-`posts_controller` and its respective view.
+As we did before, we need to add the `show` action in
+`app/controllers/posts_controller.rb` and its respective view.
```ruby
def show
@@ -1216,7 +1216,7 @@ This command will generate four files:
| test/models/comment_test.rb | Testing harness for the comments model |
| test/fixtures/comments.yml | Sample comments for use in testing |
-First, take a look at `comment.rb`:
+First, take a look at `app/models/comment.rb`:
```ruby
class Comment < ActiveRecord::Base
@@ -1277,7 +1277,7 @@ this way:
* One post can have many comments.
In fact, this is very close to the syntax that Rails uses to declare this
-association. You've already seen the line of code inside the `Comment` model that
+association. You've already seen the line of code inside the `Comment` model (app/models/comment.rb) that
makes each comment belong to a Post:
```ruby
@@ -1286,7 +1286,7 @@ class Comment < ActiveRecord::Base
end
```
-You'll need to edit the `post.rb` file to add the other side of the association:
+You'll need to edit `app/models/post.rb` to add the other side of the association:
```ruby
class Post < ActiveRecord::Base
@@ -1350,7 +1350,7 @@ the post show page to see their comment now listed. Due to this, our
spam comments when they arrive.
So first, we'll wire up the Post show template
-(`/app/views/posts/show.html.erb`) to let us make a new comment:
+(`app/views/posts/show.html.erb`) to let us make a new comment:
```html+erb
<p>
@@ -1609,7 +1609,7 @@ So first, let's add the delete link in the
Clicking this new "Destroy Comment" link will fire off a `DELETE
/posts/:post_id/comments/:id` to our `CommentsController`, which can then use
this to find the comment we want to delete, so let's add a destroy action to our
-controller:
+controller (`app/controllers/comments_controller.rb`):
```ruby
class CommentsController < ApplicationController
@@ -1667,7 +1667,7 @@ action if that method allows it.
To use the authentication system, we specify it at the top of our
`PostsController`, in this case, we want the user to be authenticated on every
-action, except for `index` and `show`, so we write that:
+action, except for `index` and `show`, so we write that in `app/controllers/posts_controller.rb`:
```ruby
class PostsController < ApplicationController
@@ -1682,7 +1682,7 @@ class PostsController < ApplicationController
```
We also only want to allow authenticated users to delete comments, so in the
-`CommentsController` we write:
+`CommentsController` (`app/controllers/comments_controller.rb`) we write:
```ruby
class CommentsController < ApplicationController
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index e1cf21f039..399a4963d7 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -134,10 +134,10 @@ However, you would probably like to **provide support for more locales** in your
WARNING: You may be tempted to store the chosen locale in a _session_ or a <em>cookie</em>, however **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [<em>RESTful</em>](http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in [Stefan Tilkov's articles](http://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below.
-The _setting part_ is easy. You can set the locale in a `before_filter` in the `ApplicationController` like this:
+The _setting part_ is easy. You can set the locale in a `before_action` in the `ApplicationController` like this:
```ruby
-before_filter :set_locale
+before_action :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
@@ -160,7 +160,7 @@ One option you have is to set the locale from the domain name where your applica
You can implement it like this in your `ApplicationController`:
```ruby
-before_filter :set_locale
+before_action :set_locale
def set_locale
I18n.locale = extract_locale_from_tld || I18n.default_locale
@@ -203,7 +203,7 @@ This solution has aforementioned advantages, however, you may not be able or may
### Setting the Locale from the URL Params
-The most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the `I18n.locale = params[:locale]` _before_filter_ in the first example. We would like to have URLs like `www.example.com/books?locale=ja` or `www.example.com/ja/books` in this case.
+The most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the `I18n.locale = params[:locale]` _before_action_ in the first example. We would like to have URLs like `www.example.com/books?locale=ja` or `www.example.com/ja/books` in this case.
This approach has almost the same set of advantages as setting the locale from the domain name: namely that it's RESTful and in accord with the rest of the World Wide Web. It does require a little bit more work to implement, though.
@@ -696,7 +696,7 @@ en:
long: "%B %d, %Y"
```
-So, all of the following equivalent lookups will return the `:short` date format `"%B %d"`:
+So, all of the following equivalent lookups will return the `:short` date format `"%b %d"`:
```ruby
I18n.t 'date.formats.short'
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index 7b1ca9ea90..829cbf2873 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -15,36 +15,25 @@ After reading this guide, you will know:
--------------------------------------------------------------------------------
-What are Migrations?
---------------------
+Migration Overview
+------------------
-Migrations are a convenient way for you to alter your database in a structured
-and organized manner. You could edit fragments of SQL by hand but you would then
-be responsible for telling other developers that they need to go and run them.
-You'd also have to keep track of which changes need to be run against the
-production machines next time you deploy.
+Migrations are a convenient way to alter your database schema over time in a
+consistent and easy way. They use a Ruby DSL so that you don't have to write
+SQL by hand, allowing your schema and changes to be database independent.
-Active Record tracks which migrations have already been run so all you have to
-do is update your source and run `rake db:migrate`. Active Record will work out
-which migrations should be run. Active Record will also update your
+You can think of each migration as being a new 'version' of the database. A
+schema starts off with nothing in it, and each migration modifies it to add or
+remove tables, columns, or entries. Active Record knows how to update your
+schema along this timeline, bringing it from whatever point it is in the
+history to the latest version. Active Record will also update your
`db/schema.rb` file to match the up-to-date structure of your database.
-Migrations also allow you to describe these transformations using Ruby. The
-great thing about this is that (like most of Active Record's functionality) it
-is database independent: you don't need to worry about the precise syntax of
-`CREATE TABLE` any more than you worry about variations on `SELECT *` (you can
-drop down to raw SQL for database specific features). For example, you could use
-SQLite3 in development, but MySQL in production.
-
-Anatomy of a Migration
-----------------------
-
-Before we dive into the details of a migration, here are a few examples of the
-sorts of things you can do:
+Here's an example of a migration:
```ruby
class CreateProducts < ActiveRecord::Migration
- def up
+ def change
create_table :products do |t|
t.string :name
t.text :description
@@ -52,102 +41,49 @@ class CreateProducts < ActiveRecord::Migration
t.timestamps
end
end
-
- def down
- drop_table :products
- end
end
```
-This migration adds a table called `products` with a string column called `name`
-and a text column called `description`. A primary key column called `id` will
-also be added, however since this is the default we do not need to explicitly specify it.
-The timestamp columns `created_at` and `updated_at` which Active Record
-populates automatically will also be added. Reversing this migration is as
-simple as dropping the table.
+This migration adds a table called `products` with a string column called
+`name` and a text column called `description`. A primary key column called `id`
+will also be added implicitly, as it's the default primary key for all Active
+Record models. The `timestamps` macro adds two columns, `created_at` and
+`updated_at`. These special columns are automatically managed by Active Record
+if they exist.
+
+Note that we define the change that we want to happen moving forward in time.
+Before this migration is run, there will be no table. After, the table will
+exist. Active Record knows how to reverse this migration as well: if we roll
+this migration back, it will remove the table.
+
+On databases that support transactions with statements that change the schema ,
+migrations are wrapped in a transaction. If the database does not support this
+then when a migration fails the parts of it that succeeded will not be rolled
+back. You will have to rollback the changes that were made by hand.
-Migrations are not limited to changing the schema. You can also use them to fix
-bad data in the database or populate new fields:
+If you wish for a migration to do something that Active Record doesn't know how
+to reverse, you can use `up` and `down` instead of `change`:
```ruby
-class AddReceiveNewsletterToUsers < ActiveRecord::Migration
+class ChangeProductsPrice < ActiveRecord::Migration
def up
- change_table :users do |t|
- t.boolean :receive_newsletter, default: false
+ change_table :products do |t|
+ t.string :price, null: false
end
- User.update_all receive_newsletter: true
end
-
+
def down
- remove_column :users, :receive_newsletter
- end
-end
-```
-
-NOTE: Some [caveats](#using-models-in-your-migrations) apply to using models in
-your migrations.
-
-This migration adds a `receive_newsletter` column to the `users` table. We want
-it to default to `false` for new users, but existing users are considered to
-have already opted in, so we use the User model to set the flag to `true` for
-existing users.
-
-### Using the change method
-
-Rails 3.1 and up makes migrations smarter by providing a `change` method.
-This method is preferred for writing constructive migrations (adding columns or
-tables). The migration knows how to migrate your database and reverse it when
-the migration is rolled back without the need to write a separate `down` method.
-
-```ruby
-class CreateProducts < ActiveRecord::Migration
- def change
- create_table :products do |t|
- t.string :name
- t.text :description
-
- t.timestamps
+ change_table :products do |t|
+ t.integer :price, null: false
end
end
end
```
-### Migrations are Classes
-
-A migration is a subclass of `ActiveRecord::Migration` that implements
-two methods: `up` (perform the required transformations) and `down` (revert
-them).
-
-Active Record provides methods that perform common data definition tasks in a
-database independent way (you'll read about them in detail later):
-
-* `add_column`
-* `add_reference`
-* `add_index`
-* `change_column`
-* `change_table`
-* `create_table`
-* `create_join_table`
-* `drop_table`
-* `remove_column`
-* `remove_index`
-* `rename_column`
-* `remove_reference`
-
-If you need to perform tasks specific to your database (e.g., create a
-[foreign key](#active-record-and-referential-integrity) constraint) then the
-`execute` method allows you to execute arbitrary SQL. A migration is just a
-regular Ruby class so you're not limited to these functions. For example, after
-adding a column you could write code to set the value of that column for
-existing records (if necessary using your models).
-
-On databases that support transactions with statements that change the schema
-(such as PostgreSQL or SQLite3), migrations are wrapped in a transaction. If the
-database does not support this (for example MySQL) then when a migration fails
-the parts of it that succeeded will not be rolled back. You will have to rollback
-the changes that were made by hand.
+Creating a Migration
+--------------------
-### What's in a Name
+### Creating a Standalone Migration
Migrations are stored as files in the `db/migrate` directory, one for each
migration class. The name of the file is of the form
@@ -157,119 +93,10 @@ of the migration. The name of the migration class (CamelCased version)
should match the latter part of the file name. For example
`20080906120000_create_products.rb` should define class `CreateProducts` and
`20080906120001_add_details_to_products.rb` should define
-`AddDetailsToProducts`. If you do feel the need to change the file name then you
-<em>have to</em> update the name of the class inside or Rails will complain
-about a missing class.
-
-Internally Rails only uses the migration's number (the timestamp) to identify
-them. Prior to Rails 2.1 the migration number started at 1 and was incremented
-each time a migration was generated. With multiple developers it was easy for
-these to clash requiring you to rollback migrations and renumber them. With
-Rails 2.1+ this is largely avoided by using the creation time of the migration
-to identify them. You can revert to the old numbering scheme by adding the
-following line to `config/application.rb`.
-
-```ruby
-config.active_record.timestamped_migrations = false
-```
-
-The combination of timestamps and recording which migrations have been run
-allows Rails to handle common situations that occur with multiple developers.
-
-For example, Alice adds migrations `20080906120000` and `20080906123000` and Bob
-adds `20080906124500` and runs it. Alice finishes her changes and checks in her
-migrations and Bob pulls down the latest changes. When Bob runs `rake db:migrate`,
-Rails knows that it has not run Alice's two migrations so it executes the `up` method for each migration.
-
-Of course this is no substitution for communication within the team. For
-example, if Alice's migration removed a table that Bob's migration assumed to
-exist, then trouble would certainly strike.
-
-### Changing Migrations
+`AddDetailsToProducts`.
-Occasionally you will make a mistake when writing a migration. If you have
-already run the migration then you cannot just edit the migration and run the
-migration again: Rails thinks it has already run the migration and so will do
-nothing when you run `rake db:migrate`. You must rollback the migration (for
-example with `rake db:rollback`), edit your migration and then run `rake db:migrate` to run the corrected version.
-
-In general, editing existing migrations is not a good idea. You will be creating
-extra work for yourself and your co-workers and cause major headaches if the
-existing version of the migration has already been run on production machines.
-Instead, you should write a new migration that performs the changes you require.
-Editing a freshly generated migration that has not yet been committed to source
-control (or, more generally, which has not been propagated beyond your
-development machine) is relatively harmless.
-
-### Supported Types
-
-Active Record supports the following database column types:
-
-* `:binary`
-* `:boolean`
-* `:date`
-* `:datetime`
-* `:decimal`
-* `:float`
-* `:integer`
-* `:primary_key`
-* `:string`
-* `:text`
-* `:time`
-* `:timestamp`
-
-These will be mapped onto an appropriate underlying database type. For example,
-with MySQL the type `:string` is mapped to `VARCHAR(255)`. You can create
-columns of types not supported by Active Record when using the non-sexy syntax such as
-
-```ruby
-create_table :products do |t|
- t.column :name, 'polygon', null: false
-end
-```
-
-This may however hinder portability to other databases.
-
-Creating a Migration
---------------------
-
-### Creating a Model
-
-The model and scaffold generators will create migrations appropriate for adding
-a new model. This migration will already contain instructions for creating the
-relevant table. If you tell Rails what columns you want, then statements for
-adding these columns will also be created. For example, running
-
-```bash
-$ rails generate model Product name:string description:text
-```
-
-TIP: All lines starting with a dollar sign `$` are intended to be run on the command line.
-
-will create a migration that looks like this
-
-```ruby
-class CreateProducts < ActiveRecord::Migration
- def change
- create_table :products do |t|
- t.string :name
- t.text :description
-
- t.timestamps
- end
- end
-end
-```
-
-You can append as many column name/type pairs as you want. By default, the
-generated migration will include `t.timestamps` (which creates the
-`updated_at` and `created_at` columns that are automatically populated
-by Active Record).
-
-### Creating a Standalone Migration
-
-If you are creating migrations for other purposes (e.g., to add a column
-to an existing table) then you can also use the migration generator:
+Of course, calculating timestamps is no fun, so Active Record provides a
+generator to handle making it for you:
```bash
$ rails generate migration AddPartNumberToProducts
@@ -344,10 +171,11 @@ or remove from it as you see fit by editing the
`db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb` file.
NOTE: The generated migration file for destructive migrations will still be
-old-style using the `up` and `down` methods. This is because Rails needs to know
-the original data types defined when you made the original changes.
+old-style using the `up` and `down` methods. This is because Rails needs to
+know the original data types defined when you made the original changes.
-Also, the generator accepts column type as `references`(also available as `belongs_to`). For instance
+Also, the generator accepts column type as `references`(also available as
+`belongs_to`). For instance
```bash
$ rails generate migration AddUserRefToProducts user:references
@@ -363,12 +191,40 @@ class AddUserRefToProducts < ActiveRecord::Migration
end
```
-This migration will create a user_id column and appropriate index.
+This migration will create a `user_id` column and appropriate index.
+
+### Model Generators
+
+The model and scaffold generators will create migrations appropriate for adding
+a new model. This migration will already contain instructions for creating the
+relevant table. If you tell Rails what columns you want, then statements for
+adding these columns will also be created. For example, running
+
+```bash
+$ rails generate model Product name:string description:text
+```
+
+will create a migration that looks like this
+
+```ruby
+class CreateProducts < ActiveRecord::Migration
+ def change
+ create_table :products do |t|
+ t.string :name
+ t.text :description
+
+ t.timestamps
+ end
+ end
+end
+```
+
+You can append as many column name/type pairs as you want.
### Supported Type Modifiers
-You can also specify some options just after the field type between curly braces. You can use the
-following modifiers:
+You can also specify some options just after the field type between curly
+braces. You can use the following modifiers:
* `limit` Sets the maximum size of the `string/text/binary/integer` fields
* `precision` Defines the precision for the `decimal` fields
@@ -400,8 +256,9 @@ get to work!
### Creating a Table
-Migration method `create_table` will be one of your workhorses. A typical use
-would be
+The `create_table` method is one of the most fundamental, but most of the time,
+will be generated for you from using a model or scaffold generator. A typical
+use would be
```ruby
create_table :products do |t|
@@ -412,31 +269,11 @@ end
which creates a `products` table with a column called `name` (and as discussed
below, an implicit `id` column).
-The object yielded to the block allows you to create columns on the table. There
-are two ways of doing it. The first (traditional) form looks like
-
-```ruby
-create_table :products do |t|
- t.column :name, :string, null: false
-end
-```
-
-The second form, the so called "sexy" migration, drops the somewhat redundant
-`column` method. Instead, the `string`, `integer`, etc. methods create a column
-of that type. Subsequent parameters are the same.
-
-```ruby
-create_table :products do |t|
- t.string :name, null: false
-end
-```
-
By default, `create_table` will create a primary key called `id`. You can change
the name of the primary key with the `:primary_key` option (don't forget to
-update the corresponding model) or, if you don't want a primary key at all (for
-example for a HABTM join table), you can pass the option `id: false`. If you
-need to pass database specific options you can place an SQL fragment in the
-`:options` option. For example,
+update the corresponding model) or, if you don't want a primary key at all, you
+can pass the option `id: false`. If you need to pass database specific options
+you can place an SQL fragment in the `:options` option. For example,
```ruby
create_table :products, options: "ENGINE=BLACKHOLE" do |t|
@@ -456,10 +293,12 @@ would be
create_join_table :products, :categories
```
-which creates a `categories_products` table with two columns called `category_id` and `product_id`.
-These columns have the option `:null` set to `false` by default.
+which creates a `categories_products` table with two columns called
+`category_id` and `product_id`. These columns have the option `:null` set to
+`false` by default.
-You can pass the option `:table_name` with you want to customize the table name. For example,
+You can pass the option `:table_name` with you want to customize the table
+name. For example,
```ruby
create_join_table :products, :categories, table_name: :categorization
@@ -467,20 +306,21 @@ create_join_table :products, :categories, table_name: :categorization
will create a `categorization` table.
-By default, `create_join_table` will create two columns with no options, but you can specify these
-options using the `:column_options` option. For example,
+By default, `create_join_table` will create two columns with no options, but
+you can specify these options using the `:column_options` option. For example,
```ruby
create_join_table :products, :categories, column_options: {null: true}
```
-will create the `product_id` and `category_id` with the `:null` option as `true`.
+will create the `product_id` and `category_id` with the `:null` option as
+`true`.
### Changing Tables
A close cousin of `create_table` is `change_table`, used for changing existing
-tables. It is used in a similar fashion to `create_table` but the object yielded
-to the block knows more tricks. For example
+tables. It is used in a similar fashion to `create_table` but the object
+yielded to the block knows more tricks. For example
```ruby
change_table :products do |t|
@@ -494,67 +334,15 @@ end
removes the `description` and `name` columns, creates a `part_number` string
column and adds an index on it. Finally it renames the `upccode` column.
-### Special Helpers
-
-Active Record provides some shortcuts for common functionality. It is for
-example very common to add both the `created_at` and `updated_at` columns and so
-there is a method that does exactly that:
-
-```ruby
-create_table :products do |t|
- t.timestamps
-end
-```
-
-will create a new products table with those two columns (plus the `id` column)
-whereas
-
-```ruby
-change_table :products do |t|
- t.timestamps
-end
-```
-adds those columns to an existing table.
-
-Another helper is called `references` (also available as `belongs_to`). In its
-simplest form it just adds some readability.
-
-```ruby
-create_table :products do |t|
- t.references :category
-end
-```
-
-will create a `category_id` column of the appropriate type. Note that you pass
-the model name, not the column name. Active Record adds the `_id` for you. If
-you have polymorphic `belongs_to` associations then `references` will add both
-of the columns required:
+### When Helpers Aren't Enough
-```ruby
-create_table :products do |t|
- t.references :attachment, polymorphic: {default: 'Photo'}
-end
-```
-
-will add an `attachment_id` column and a string `attachment_type` column with
-a default value of 'Photo'. `references` also allows you to define an
-index directly, instead of using `add_index` after the `create_table` call:
+If the helpers provided by Active Record aren't enough you can use the `execute`
+method to execute arbitrary SQL:
```ruby
-create_table :products do |t|
- t.references :category, index: true
-end
+Products.connection.execute('UPDATE `products` SET `price`=`free` WHERE 1')
```
-will create an index identical to calling `add_index :products, :category_id`.
-
-NOTE: The `references` helper does not actually create foreign key constraints
-for you. You will need to use `execute` or a plugin that adds [foreign key
-support](#active-record-and-referential-integrity).
-
-If the helpers provided by Active Record aren't enough you can use the `execute`
-method to execute arbitrary SQL.
-
For more details and examples of individual methods, check the API documentation.
In particular the documentation for
[`ActiveRecord::ConnectionAdapters::SchemaStatements`](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html)
@@ -567,9 +355,10 @@ and
### Using the `change` Method
-The `change` method removes the need to write both `up` and `down` methods in
-those cases that Rails knows how to revert the changes automatically. Currently,
-the `change` method supports only these migration definitions:
+The `change` method is the primary way of writing migrations. It works for the
+majority of cases, where Active Record knows how to reverse the migration
+automatically. Currently, the `change` method supports only these migration
+definitions:
* `add_column`
* `add_index`
@@ -585,12 +374,13 @@ If you're going to need to use any other methods, you'll have to write the
### Using the `up`/`down` Methods
-The `down` method of your migration should revert the transformations done by
-the `up` method. In other words, the database schema should be unchanged if you
-do an `up` followed by a `down`. For example, if you create a table in the `up`
-method, you should drop it in the `down` method. It is wise to reverse the
-transformations in precisely the reverse order they were made in the `up`
-method. For example,
+The `up` method should describe the transformation you'd like to make to your
+schema, and the `down` method of your migration should revert the
+transformations done by the `up` method. In other words, the database schema
+should be unchanged if you do an `up` followed by a `down`. For example, if you
+create a table in the `up` method, you should drop it in the `down` method. It
+is wise to reverse the transformations in precisely the reverse order they were
+made in the `up` method. For example,
```ruby
class ExampleMigration < ActiveRecord::Migration
@@ -598,6 +388,7 @@ class ExampleMigration < ActiveRecord::Migration
create_table :products do |t|
t.references :category
end
+
#add a foreign key
execute <<-SQL
ALTER TABLE products
@@ -605,6 +396,7 @@ class ExampleMigration < ActiveRecord::Migration
FOREIGN KEY (category_id)
REFERENCES categories(id)
SQL
+
add_column :users, :home_page_url, :string
rename_column :users, :email, :email_address
end
@@ -612,10 +404,12 @@ class ExampleMigration < ActiveRecord::Migration
def down
rename_column :users, :email_address, :email
remove_column :users, :home_page_url
+
execute <<-SQL
ALTER TABLE products
DROP FOREIGN KEY fk_products_categories
SQL
+
drop_table :products
end
end
@@ -630,17 +424,16 @@ can't be done.
Running Migrations
------------------
-Rails provides a set of rake tasks to work with migrations which boil down to
-running certain sets of migrations.
+Rails provides a set of Rake tasks to run certain sets of migrations.
-The very first migration related rake task you will use will probably be
+The very first migration related Rake task you will use will probably be
`rake db:migrate`. In its most basic form it just runs the `up` or `change`
method for all the migrations that have not yet been run. If there are
no such migrations, it exits. It will run these migrations in order based
on the date of the migration.
Note that running the `db:migrate` also invokes the `db:schema:dump` task, which
-will update your db/schema.rb file to match the structure of your database.
+will update your `db/schema.rb` file to match the structure of your database.
If you specify a target version, Active Record will run the required migrations
(up, down or change) until it has reached the specified version. The version
@@ -693,9 +486,10 @@ version to migrate to.
The `rake db:reset` task will drop the database, recreate it and load the
current schema into it.
-NOTE: This is not the same as running all the migrations. It will only use the contents
-of the current schema.rb file. If a migration can't be rolled back, 'rake db:reset'
-may not help you. To find out more about dumping the schema see [schema.rb](#schema-dumping-and-you).
+NOTE: This is not the same as running all the migrations. It will only use the
+contents of the current schema.rb file. If a migration can't be rolled back,
+'rake db:reset' may not help you. To find out more about dumping the schema see
+'[schema dumping and you](#schema-dumping-and-you).'
### Running Specific Migrations
@@ -708,13 +502,16 @@ example,
$ rake db:migrate:up VERSION=20080906120000
```
-will run the `up` method from the 20080906120000 migration. This task will first
-check whether the migration is already performed and will do nothing if Active Record believes
-that it has already been run.
+will run the `up` method from the 20080906120000 migration. This task will
+first check whether the migration is already performed and will do nothing if
+Active Record believes that it has already been run.
### Running Migrations in Different Environments
-By default running `rake db:migrate` will run in the `development` environment. To run migrations against another environment you can specify it using the `RAILS_ENV` environment variable while running the command. For example to run migrations against the `test` environment you could run:
+By default running `rake db:migrate` will run in the `development` environment.
+To run migrations against another environment you can specify it using the
+`RAILS_ENV` environment variable while running the command. For example to run
+migrations against the `test` environment you could run:
```bash
$ rake db:migrate RAILS_ENV=test
@@ -752,9 +549,12 @@ class CreateProducts < ActiveRecord::Migration
t.timestamps
end
end
+
say "Created a table"
+
suppress_messages {add_index :products, :name}
say "and an index!", true
+
say_with_time 'Waiting for a while' do
sleep 10
250
@@ -778,11 +578,29 @@ generates the following output
If you want Active Record to not output anything, then running `rake db:migrate
VERBOSE=false` will suppress all output.
+Changing Existing Migrations
+----------------------------
+
+Occasionally you will make a mistake when writing a migration. If you have
+already run the migration then you cannot just edit the migration and run the
+migration again: Rails thinks it has already run the migration and so will do
+nothing when you run `rake db:migrate`. You must rollback the migration (for
+example with `rake db:rollback`), edit your migration and then run `rake
+db:migrate` to run the corrected version.
+
+In general, editing existing migrations is not a good idea. You will be
+creating extra work for yourself and your co-workers and cause major headaches
+if the existing version of the migration has already been run on production
+machines. Instead, you should write a new migration that performs the changes
+you require. Editing a freshly generated migration that has not yet been
+committed to source control (or, more generally, which has not been propagated
+beyond your development machine) is relatively harmless.
+
Using Models in Your Migrations
-------------------------------
-When creating or updating data in a migration it is often tempting to use one of
-your models. After all, they exist to provide easy access to the underlying
+When creating or updating data in a migration it is often tempting to use one
+of your models. After all, they exist to provide easy access to the underlying
data. This can be done, but some caution should be observed.
For example, problems occur when the model uses database columns which are (1)
@@ -795,7 +613,7 @@ which contains a `Product` model:
Bob goes on vacation.
Alice creates a migration for the `products` table which adds a new column and
-initializes it. She also adds a validation to the `Product` model for the new
+initializes it. She also adds a validation to the `Product` model for the new
column.
```ruby
@@ -844,8 +662,8 @@ Both migrations work for Alice.
Bob comes back from vacation and:
-* Updates the source - which contains both migrations and the latest version of
- the Product model.
+* Updates the source - which contains both migrations and the latest version
+ of the Product model.
* Runs outstanding migrations with `rake db:migrate`, which
includes the one that updates the `Product` model.
@@ -860,10 +678,10 @@ An error has occurred, this and all later migrations canceled:
undefined method `fuzz' for #<Product:0x000001049b14a0>
```
-A fix for this is to create a local model within the migration. This keeps Rails
-from running the validations, so that the migrations run to completion.
+A fix for this is to create a local model within the migration. This keeps
+Rails from running the validations, so that the migrations run to completion.
-When using a faux model, it's a good idea to call
+When using a local model, it's a good idea to call
`Product.reset_column_information` to refresh the `ActiveRecord` cache for the
`Product` model prior to updating data in the database.
@@ -902,20 +720,20 @@ end
There are other ways in which the above example could have gone badly.
For example, imagine that Alice creates a migration that selectively
-updates the +description+ field on certain products. She runs the
+updates the `description` field on certain products. She runs the
migration, commits the code, and then begins working on the next feature,
-which is to add a new column +fuzz+ to the products table.
+which is to add a new column `fuzz` to the products table.
She creates two migrations for this new feature, one which adds the new
-column, and a second which selectively updates the +fuzz+ column based on
+column, and a second which selectively updates the `fuzz` column based on
other product attributes.
These migrations run just fine, but when Bob comes back from his vacation
and calls `rake db:migrate` to run all the outstanding migrations, he gets a
-subtle bug: The descriptions have defaults, and the +fuzz+ column is present,
-but +fuzz+ is nil on all products.
+subtle bug: The descriptions have defaults, and the `fuzz` column is present,
+but `fuzz` is nil on all products.
-The solution is again to use +Product.reset_column_information+ before
+The solution is again to use `Product.reset_column_information` before
referencing the Product model in a migration, ensuring the Active Record's
knowledge of the table structure is current before manipulating data in those
records.
@@ -948,12 +766,13 @@ you desire that functionality.
### Types of Schema Dumps
-There are two ways to dump the schema. This is set in `config/application.rb` by
-the `config.active_record.schema_format` setting, which may be either `:sql` or
-`:ruby`.
+There are two ways to dump the schema. This is set in `config/application.rb`
+by the `config.active_record.schema_format` setting, which may be either `:sql`
+or `:ruby`.
If `:ruby` is selected then the schema is stored in `db/schema.rb`. If you look
-at this file you'll find that it looks an awful lot like one very big migration:
+at this file you'll find that it looks an awful lot like one very big
+migration:
```ruby
ActiveRecord::Schema.define(version: 20080906171750) do
@@ -976,8 +795,8 @@ end
In many ways this is exactly what it is. This file is created by inspecting the
database and expressing its structure using `create_table`, `add_index`, and so
on. Because this is database-independent, it could be loaded into any database
-that Active Record supports. This could be very useful if you were to distribute
-an application that is able to run against multiple databases.
+that Active Record supports. This could be very useful if you were to
+distribute an application that is able to run against multiple databases.
There is however a trade-off: `db/schema.rb` cannot express database specific
items such as foreign key constraints, triggers, or stored procedures. While in
@@ -985,11 +804,11 @@ a migration you can execute custom SQL statements, the schema dumper cannot
reconstitute those statements from the database. If you are using features like
this, then you should set the schema format to `:sql`.
-Instead of using Active Record's schema dumper, the database's structure will be
-dumped using a tool specific to the database (via the `db:structure:dump` Rake task)
-into `db/structure.sql`. For example, for the PostgreSQL RDBMS, the
-`pg_dump` utility is used. For MySQL, this file will contain the output of
-`SHOW CREATE TABLE` for the various tables.
+Instead of using Active Record's schema dumper, the database's structure will
+be dumped using a tool specific to the database (via the `db:structure:dump`
+Rake task) into `db/structure.sql`. For example, for PostgreSQL, the `pg_dump`
+utility is used. For MySQL, this file will contain the output of `SHOW CREATE
+TABLE` for the various tables.
Loading these schemas is simply a question of executing the SQL statements they
contain. By definition, this will create a perfect copy of the database's
@@ -1010,14 +829,47 @@ which push some of that intelligence back into the database, are not heavily
used.
Validations such as `validates :foreign_key, uniqueness: true` are one way in
-which models can enforce data integrity. The `:dependent` option on associations
-allows models to automatically destroy child objects when the parent is
-destroyed. Like anything which operates at the application level, these cannot
-guarantee referential integrity and so some people augment them with foreign key
-constraints in the database.
-
-Although Active Record does not provide any tools for working directly with such
-features, the `execute` method can be used to execute arbitrary SQL. You could
-also use some plugin like [foreigner](https://github.com/matthuhiggins/foreigner)
-which add foreign key support to Active Record (including support for dumping
-foreign keys in `db/schema.rb`).
+which models can enforce data integrity. The `:dependent` option on
+associations allows models to automatically destroy child objects when the
+parent is destroyed. Like anything which operates at the application level,
+these cannot guarantee referential integrity and so some people augment them
+with foreign key constraints in the database.
+
+Although Active Record does not provide any tools for working directly with
+such features, the `execute` method can be used to execute arbitrary SQL. You
+could also use some plugin like
+[foreigner](https://github.com/matthuhiggins/foreigner) which add foreign key
+support to Active Record (including support for dumping foreign keys in
+`db/schema.rb`).
+
+Migrations and Seed Data
+------------------------
+
+Some people use migrations to add data to the database:
+
+```ruby
+class AddInitialProducts < ActiveRecord::Migration
+ def up
+ 5.times do |i|
+ Product.create(name: "Product ##{i}", description: "A product.")
+ end
+ end
+
+ def down
+ Product.delete_all
+ end
+end
+```
+
+However, Rails has a 'seeds' feature that should be used for seeding a database
+with initial data. It's a really simple feature: just fill up `db/seeds.rb`
+with some Ruby code, and run `rake db:seed`:
+
+```ruby
+5.times do |i|
+ Product.create(name: "Product ##{i}", description: "A product.")
+end
+```
+
+This is generally a much cleaner way to set up the database of a blank
+application.
diff --git a/guides/source/performance_testing.md b/guides/source/performance_testing.md
index b84c5d1732..a07f64ec29 100644
--- a/guides/source/performance_testing.md
+++ b/guides/source/performance_testing.md
@@ -559,9 +559,9 @@ Usage: rails profiler 'Ruby.code' 'Ruby.more_code' ... [OPTS]
Default: 1
-o, --output PATH Directory to use when writing the results.
Default: tmp/performance
- --metrics a,b,c Metrics to use.
+ -m, --metrics a,b,c Metrics to use.
Default: process_time,memory,objects
- -m, --formats x,y,z Formats to output to.
+ -f, --formats x,y,z Formats to output to.
Default: flat,graph_html,call_tree
```
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 7d43854f2f..714c0b609e 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -20,13 +20,13 @@ The Rails router recognizes URLs and dispatches them to a controller's action. I
### Connecting URLs to Code
-When your Rails application receives an incoming request
+When your Rails application receives an incoming request for:
```
GET /patients/17
```
-it asks the router to match it to a controller action. If the first matching route is
+it asks the router to match it to a controller action. If the first matching route is:
```ruby
get '/patients/:id', to: 'patients#show'
@@ -36,23 +36,25 @@ the request is dispatched to the `patients` controller's `show` action with `{ i
### Generating Paths and URLs from Code
-You can also generate paths and URLs. If the route above is modified to be
+You can also generate paths and URLs. If the route above is modified to be:
```ruby
get '/patients/:id', to: 'patients#show', as: 'patient'
```
-If your application contains this code:
+and your application contains this code in the controller:
```ruby
@patient = Patient.find(17)
```
+and this in the corresponding view:
+
```erb
<%= link_to 'Patient Record', patient_path(@patient) %>
```
-The router will generate the path `/patients/17`. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper.
+then the router will generate the path `/patients/17`. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper.
Resource Routing: the Rails Default
-----------------------------------
@@ -63,13 +65,13 @@ Resource routing allows you to quickly declare all of the common routes for a gi
Browsers request pages from Rails by making a request for a URL using a specific HTTP method, such as `GET`, `POST`, `PATCH`, `PUT` and `DELETE`. Each method is a request to perform an operation on the resource. A resource route maps a number of related requests to actions in a single controller.
-When your Rails application receives an incoming request for
+When your Rails application receives an incoming request for:
```
DELETE /photos/17
```
-it asks the router to map it to a controller action. If the first matching route is
+it asks the router to map it to a controller action. If the first matching route is:
```ruby
resources :photos
@@ -79,7 +81,7 @@ Rails would dispatch that request to the `destroy` method on the `photos` contro
### CRUD, Verbs, and Actions
-In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as
+In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as:
```ruby
resources :photos
@@ -87,7 +89,7 @@ resources :photos
creates seven different routes in your application, all mapping to the `Photos` controller:
-| HTTP Verb | Path | action | used for |
+| HTTP Verb | Path | Action | Used for |
| --------- | ---------------- | ------- | -------------------------------------------- |
| GET | /photos | index | display a list of all photos |
| GET | /photos/new | new | return an HTML form for creating a new photo |
@@ -97,9 +99,11 @@ creates seven different routes in your application, all mapping to the `Photos`
| PATCH/PUT | /photos/:id | update | update a specific photo |
| DELETE | /photos/:id | destroy | delete a specific photo |
+NOTE: Because the router uses the HTTP verb and URL to match inbound requests, four URLs map to seven different actions.
+
NOTE: Rails routes are matched in the order they are specified, so if you have a `resources :photos` above a `get 'photos/poll'` the `show` action's route for the `resources` line will be matched before the `get` line. To fix this, move the `get` line **above** the `resources` line so that it is matched first.
-### Paths and URLs
+### Path and URL Helpers
Creating a resourceful route will also expose a number of helpers to the controllers in your application. In the case of `resources :photos`:
@@ -110,8 +114,6 @@ Creating a resourceful route will also expose a number of helpers to the control
Each of these helpers has a corresponding `_url` helper (such as `photos_url`) which returns the same path prefixed with the current host, port and path prefix.
-NOTE: Because the router uses the HTTP verb and URL to match inbound requests, four URLs map to seven different actions.
-
### Defining Multiple Resources at the Same Time
If you need to create routes for more than one resource, you can save a bit of typing by defining them all with a single call to `resources`:
@@ -120,7 +122,7 @@ If you need to create routes for more than one resource, you can save a bit of t
resources :photos, :books, :videos
```
-This works exactly the same as
+This works exactly the same as:
```ruby
resources :photos
@@ -130,13 +132,13 @@ resources :videos
### Singular Resources
-Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like `/profile` to always show the profile of the currently logged in user. In this case, you can use a singular resource to map `/profile` (rather than `/profile/:id`) to the `show` action.
+Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like `/profile` to always show the profile of the currently logged in user. In this case, you can use a singular resource to map `/profile` (rather than `/profile/:id`) to the `show` action:
```ruby
get 'profile', to: 'users#show'
```
-This resourceful route
+This resourceful route:
```ruby
resource :geocoder
@@ -144,7 +146,7 @@ resource :geocoder
creates six different routes in your application, all mapping to the `Geocoders` controller:
-| HTTP Verb | Path | action | used for |
+| HTTP Verb | Path | Action | Used for |
| --------- | -------------- | ------- | --------------------------------------------- |
| GET | /geocoder/new | new | return an HTML form for creating the geocoder |
| POST | /geocoder | create | create the new geocoder |
@@ -175,7 +177,7 @@ end
This will create a number of routes for each of the `posts` and `comments` controller. For `Admin::PostsController`, Rails will create:
-| HTTP Verb | Path | action | used for |
+| HTTP Verb | Path | Action | Used for |
| --------- | --------------------- | ------- | ------------------------- |
| GET | /admin/posts | index | admin_posts_path |
| GET | /admin/posts/new | new | new_admin_post_path |
@@ -185,7 +187,7 @@ This will create a number of routes for each of the `posts` and `comments` contr
| PATCH/PUT | /admin/posts/:id | update | admin_post_path(:id) |
| DELETE | /admin/posts/:id | destroy | admin_post_path(:id) |
-If you want to route `/posts` (without the prefix `/admin`) to `Admin::PostsController`, you could use
+If you want to route `/posts` (without the prefix `/admin`) to `Admin::PostsController`, you could use:
```ruby
scope module: 'admin' do
@@ -193,13 +195,13 @@ scope module: 'admin' do
end
```
-or, for a single case
+or, for a single case:
```ruby
resources :posts, module: 'admin'
```
-If you want to route `/admin/posts` to `PostsController` (without the `Admin::` module prefix), you could use
+If you want to route `/admin/posts` to `PostsController` (without the `Admin::` module prefix), you could use:
```ruby
scope '/admin' do
@@ -207,7 +209,7 @@ scope '/admin' do
end
```
-or, for a single case
+or, for a single case:
```ruby
resources :posts, path: '/admin/posts'
@@ -215,7 +217,7 @@ resources :posts, path: '/admin/posts'
In each of these cases, the named routes remain the same as if you did not use `scope`. In the last case, the following paths map to `PostsController`:
-| HTTP Verb | Path | action | named helper |
+| HTTP Verb | Path | Action | Named Helper |
| --------- | --------------------- | ------- | ------------------- |
| GET | /admin/posts | index | posts_path |
| GET | /admin/posts/new | new | new_post_path |
@@ -249,7 +251,7 @@ end
In addition to the routes for magazines, this declaration will also route ads to an `AdsController`. The ad URLs require a magazine:
-| HTTP Verb | Path | action | used for |
+| HTTP Verb | Path | Action | Used for |
| --------- | ------------------------------------ | ------- | -------------------------------------------------------------------------- |
| GET | /magazines/:magazine_id/ads | index | display a list of all ads for a specific magazine |
| GET | /magazines/:magazine_id/ads/new | new | return an HTML form for creating a new ad belonging to a specific magazine |
@@ -273,7 +275,7 @@ resources :publishers do
end
```
-Deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize paths such as
+Deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize paths such as:
```
/publishers/1/magazines/2/photos/3
@@ -283,9 +285,94 @@ The corresponding route helper would be `publisher_magazine_photo_url`, requirin
TIP: _Resources should never be nested more than 1 level deep._
+#### Shallow Nesting
+
+One way to avoid deep nesting (as recommended above) is to generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions. In other words, to only build routes with the minimal amount of information to uniquely identify the resource, like this:
+
+```ruby
+resources :posts do
+ resources :comments, only: [:index, :new, :create]
+end
+resources :comments, only: [:show, :edit, :update, :destroy]
+```
+
+This idea strikes a balance between descriptive routes and deep nesting. There exists shorthand syntax to achieve just that, via the `:shallow` option:
+
+```ruby
+resources :posts do
+ resources :comments, shallow: true
+end
+```
+
+This will generate the exact same routes as the first example. You can also specify the `:shallow` option in the parent resource, in which case all of the nested resources will be shallow:
+
+```ruby
+resources :posts, shallow: true do
+ resources :comments
+ resources :quotes
+ resources :drafts
+end
+```
+
+The `shallow` method of the DSL creates a scope inside of which every nesting is shallow. This generates the same routes as the previous example:
+
+```ruby
+shallow do
+ resources :posts do
+ resources :comments
+ resources :quotes
+ resources :drafts
+ end
+end
+```
+
+There exists two options for `scope` to customize shallow routes. `:shallow_path` prefixes member paths with the specified parameter:
+
+```ruby
+scope shallow_path: "sekret" do
+ resources :posts do
+ resources :comments, shallow: true
+ end
+end
+```
+
+The comments resource here will have the following routes generated for it:
+
+| HTTP Verb | Path | Named Helper |
+| --------- | -------------------------------------- | ------------------- |
+| GET | /posts/:post_id/comments(.:format) | post_comments |
+| POST | /posts/:post_id/comments(.:format) | post_comments |
+| GET | /posts/:post_id/comments/new(.:format) | new_post_comment |
+| GET | /sekret/comments/:id/edit(.:format) | edit_comment |
+| GET | /sekret/comments/:id(.:format) | comment |
+| PATCH/PUT | /sekret/comments/:id(.:format) | comment |
+| DELETE | /sekret/comments/:id(.:format) | comment |
+
+The `:shallow_prefix` option adds the specified parameter to the named helpers:
+
+```ruby
+scope shallow_prefix: "sekret" do
+ resources :posts do
+ resources :comments, shallow: true
+ end
+end
+```
+
+The comments resource here will have the following routes generated for it:
+
+| HTTP Verb | Path | Named Helper |
+| --------- | -------------------------------------- | ------------------- |
+| GET | /posts/:post_id/comments(.:format) | post_comments |
+| POST | /posts/:post_id/comments(.:format) | post_comments |
+| GET | /posts/:post_id/comments/new(.:format) | new_post_comment |
+| GET | /comments/:id/edit(.:format) | edit_sekret_comment |
+| GET | /comments/:id(.:format) | sekret_comment |
+| PATCH/PUT | /comments/:id(.:format) | sekret_comment |
+| DELETE | /comments/:id(.:format) | sekret_comment |
+
### Routing concerns
-Routing Concerns allows you to declare common routes that can be reused inside others resources and routes.
+Routing Concerns allows you to declare common routes that can be reused inside others resources and routes. To define a concern:
```ruby
concern :commentable do
@@ -297,7 +384,7 @@ concern :image_attachable do
end
```
-These concerns can be used in resources to avoid code duplication and share behavior across routes.
+These concerns can be used in resources to avoid code duplication and share behavior across routes:
```ruby
resources :messages, concerns: :commentable
@@ -305,6 +392,19 @@ resources :messages, concerns: :commentable
resources :posts, concerns: [:commentable, :image_attachable]
```
+The above is equivalent to:
+
+```ruby
+resources :messages do
+ resources :comments
+end
+
+resources :posts do
+ resources :comments
+ resources :images, only: :index
+end
+```
+
Also you can use them in any place that you want inside the routes, for example in a scope or namespace call:
```ruby
@@ -323,7 +423,7 @@ resources :magazines do
end
```
-When using `magazine_ad_path`, you can pass in instances of `Magazine` and `Ad` instead of the numeric IDs.
+When using `magazine_ad_path`, you can pass in instances of `Magazine` and `Ad` instead of the numeric IDs:
```erb
<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>
@@ -415,9 +515,7 @@ end
This will enable Rails to recognize paths such as `/comments/new/preview` with GET, and route to the `preview` action of `CommentsController`. It will also create the `preview_new_comment_url` and `preview_new_comment_path` route helpers.
-#### A Note of Caution
-
-If you find yourself adding many extra actions to a resourceful route, it's time to stop and ask yourself whether you're disguising the presence of another resource.
+TIP: If you find yourself adding many extra actions to a resourceful route, it's time to stop and ask yourself whether you're disguising the presence of another resource.
Non-Resourceful Routes
----------------------
@@ -454,11 +552,11 @@ NOTE: You can't use `:namespace` or `:module` with a `:controller` path segment.
get ':controller(/:action(/:id))', controller: /admin\/[^\/]+/
```
-TIP: By default dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment, add a constraint that overrides this – for example, `id: /[^\/]+/` allows anything except a slash.
+TIP: By default, dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment, add a constraint that overrides this – for example, `id: /[^\/]+/` allows anything except a slash.
### Static Segments
-You can specify static segments when creating a route:
+You can specify static segments when creating a route by not prepending a colon to a fragment:
```ruby
get ':controller/:action/:id/with_user/:user_id'
@@ -496,7 +594,7 @@ Rails would match `photos/12` to the `show` action of `PhotosController`, and se
### Naming Routes
-You can specify a name for any route using the `:as` option.
+You can specify a name for any route using the `:as` option:
```ruby
get 'exit', to: 'sessions#destroy', as: :logout
@@ -526,7 +624,7 @@ You can match all verbs to a particular route using `via: :all`:
match 'photos', to: 'photos#show', via: :all
```
-You should avoid routing all verbs to an action unless you have a good reason to, as routing both `GET` requests and `POST` requests to a single action has security implications.
+NOTE: Routing both `GET` and `POST` requests to a single action has security implications. In general, you should avoid routing all verbs to an action unless you have a good reason to.
### Segment Constraints
@@ -536,7 +634,7 @@ You can use the `:constraints` option to enforce a format for a dynamic segment:
get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ }
```
-This route would match paths such as `/photos/A12345`. You can more succinctly express the same route this way:
+This route would match paths such as `/photos/A12345`, but not `/photos/893`. You can more succinctly express the same route this way:
```ruby
get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/
@@ -609,17 +707,17 @@ end
Both the `matches?` method and the lambda gets the `request` object as an argument.
-### Route Globbing
+### Route Globbing and Wildcard Segments
-Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example
+Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example:
```ruby
get 'photos/*other', to: 'photos#unknown'
```
-This route would match `photos/12` or `/photos/long/path/to/12`, setting `params[:other]` to `"12"` or `"long/path/to/12"`.
+This route would match `photos/12` or `/photos/long/path/to/12`, setting `params[:other]` to `"12"` or `"long/path/to/12"`. The fragments prefixed with a star are called "wildcard segments".
-Wildcard segments can occur anywhere in a route. For example,
+Wildcard segments can occur anywhere in a route. For example:
```ruby
get 'books/*section/:title', to: 'books#show'
@@ -627,7 +725,7 @@ get 'books/*section/:title', to: 'books#show'
would match `books/some/section/last-words-a-memoir` with `params[:section]` equals `'some/section'`, and `params[:title]` equals `'last-words-a-memoir'`.
-Technically a route can have even more than one wildcard segment. The matcher assigns segments to parameters in an intuitive way. For example,
+Technically, a route can have even more than one wildcard segment. The matcher assigns segments to parameters in an intuitive way. For example:
```ruby
get '*a/foo/*b', to: 'test#index'
@@ -635,7 +733,7 @@ get '*a/foo/*b', to: 'test#index'
would match `zoo/woo/foo/bar/baz` with `params[:a]` equals `'zoo/woo'`, and `params[:b]` equals `'bar/baz'`.
-NOTE: Starting from Rails 3.1, wildcard routes will always match the optional format segment by default. For example if you have this route:
+NOTE: Starting from Rails 3.1, wildcard segments will always match the optional format segment by default. For example if you have this route:
```ruby
get '*pages', to: 'pages#show'
@@ -680,7 +778,7 @@ In all of these cases, if you don't provide the leading host (`http://www.exampl
### Routing to Rack Applications
-Instead of a String, like `'posts#index'`, which corresponds to the `index` action in the `PostsController`, you can specify any <a href="rails_on_rack.html">Rack application</a> as the endpoint for a matcher.
+Instead of a String like `'posts#index'`, which corresponds to the `index` action in the `PostsController`, you can specify any <a href="rails_on_rack.html">Rack application</a> as the endpoint for a matcher:
```ruby
match '/application.js', to: Sprockets, via: :all
@@ -705,7 +803,7 @@ NOTE: The `root` route only routes `GET` requests to the action.
### Unicode character routes
-You can specify unicode character routes directly. For example
+You can specify unicode character routes directly. For example:
```ruby
get 'こんにちは', to: 'welcome#index'
@@ -726,7 +824,7 @@ resources :photos, controller: 'images'
will recognize incoming paths beginning with `/photos` but route to the `Images` controller:
-| HTTP Verb | Path | action | named helper |
+| HTTP Verb | Path | Action | Named Helper |
| --------- | ---------------- | ------- | -------------------- |
| GET | /photos | index | photos_path |
| GET | /photos/new | new | new_photo_path |
@@ -771,7 +869,7 @@ resources :photos, as: 'images'
will recognize incoming paths beginning with `/photos` and route the requests to `PhotosController`, but use the value of the :as option to name the helpers.
-| HTTP Verb | Path | action | named helper |
+| HTTP Verb | Path | Action | Named Helper |
| --------- | ---------------- | ------- | -------------------- |
| GET | /photos | index | images_path |
| GET | /photos/new | new | new_image_path |
@@ -789,7 +887,7 @@ The `:path_names` option lets you override the automatically-generated "new" and
resources :photos, path_names: { new: 'make', edit: 'change' }
```
-This would cause the routing to recognize paths such as
+This would cause the routing to recognize paths such as:
```
/photos/make
@@ -808,7 +906,7 @@ end
### Prefixing the Named Route Helpers
-You can use the `:as` option to prefix the named route helpers that Rails generates for a route. Use this option to prevent name collisions between routes using a path scope.
+You can use the `:as` option to prefix the named route helpers that Rails generates for a route. Use this option to prevent name collisions between routes using a path scope. For example:
```ruby
scope 'admin' do
@@ -876,7 +974,7 @@ end
Rails now creates routes to the `CategoriesController`.
-| HTTP Verb | Path | action | used for |
+| HTTP Verb | Path | Action | Used for |
| --------- | -------------------------- | ------- | ----------------------- |
| GET | /kategorien | index | categories_path |
| GET | /kategorien/neu | new | new_category_path |
@@ -888,7 +986,7 @@ Rails now creates routes to the `CategoriesController`.
### Overriding the Singular Form
-If you want to define the singular form of a resource, you should add additional rules to the `Inflector`.
+If you want to define the singular form of a resource, you should add additional rules to the `Inflector`:
```ruby
ActiveSupport::Inflector.inflections do |inflect|
@@ -898,7 +996,7 @@ end
### Using `:as` in Nested Resources
-The `:as` option overrides the automatically-generated name for the resource in nested route helpers. For example,
+The `:as` option overrides the automatically-generated name for the resource in nested route helpers. For example:
```ruby
resources :magazines do
@@ -913,7 +1011,7 @@ Inspecting and Testing Routes
Rails offers facilities for inspecting and testing your routes.
-### Seeing Existing Routes
+### Listing Existing Routes
To get a complete list of the available routes in your application, visit `http://localhost:3000/rails/info/routes` in your browser while your server is running in the **development** environment. You can also execute the `rake routes` command in your terminal to produce the same output.
@@ -951,7 +1049,7 @@ Routes should be included in your testing strategy (just like the rest of your a
#### The `assert_generates` Assertion
-`assert_generates` asserts that a particular set of options generate a particular path and can be used with default routes or custom routes.
+`assert_generates` asserts that a particular set of options generate a particular path and can be used with default routes or custom routes. For example:
```ruby
assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' }
@@ -960,7 +1058,7 @@ assert_generates '/about', controller: 'pages', action: 'about'
#### The `assert_recognizes` Assertion
-`assert_recognizes` is the inverse of `assert_generates`. It asserts that a given path is recognized and routes it to a particular spot in your application.
+`assert_recognizes` is the inverse of `assert_generates`. It asserts that a given path is recognized and routes it to a particular spot in your application. For example:
```ruby
assert_recognizes({ controller: 'photos', action: 'show', id: '1' }, '/photos/1')
@@ -974,7 +1072,7 @@ assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos',
#### The `assert_routing` Assertion
-The `assert_routing` assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of `assert_generates` and `assert_recognizes`.
+The `assert_routing` assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of `assert_generates` and `assert_recognizes`:
```ruby
assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' })
diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md
index 6e3173cdb4..2c3bc686ef 100644
--- a/guides/source/ruby_on_rails_guides_guidelines.md
+++ b/guides/source/ruby_on_rails_guides_guidelines.md
@@ -62,7 +62,7 @@ HTML Guides
### Generation
-To generate all the guides, just `cd` into the **`guides`** directory and execute:
+To generate all the guides, just `cd` into the **`guides`** directory, run `bundle install` and execute:
```
bundle exec rake guides:generate
@@ -74,8 +74,6 @@ or
bundle exec rake guides:generate:html
```
-(You may need to run `bundle install` first to install the required gems.)
-
To process `my_guide.md` and nothing else use the `ONLY` environment variable:
```
diff --git a/guides/source/security.md b/guides/source/security.md
index 6c32a8ff5b..532a1ae5cc 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -688,7 +688,7 @@ NOTE: _When sanitizing, protecting or verifying something, whitelists over black
A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _prefer to use whitelist approaches_:
-* Use before_filter only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions.
+* Use before_action only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions.
* Use attr_accessible instead of attr_protected. See the mass-assignment section for details
* Allow &lt;strong&gt; instead of removing &lt;script&gt; against Cross-Site Scripting (XSS). See below for details.
* Don't try to correct user input by blacklists:
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 7a68cf0a2e..01dd86c23e 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,11 @@
## Rails 4.0.0 (unreleased) ##
+* Add ENV['RACK_ENV'] support to `rails runner/console/server`.
+
+ *kennyj*
+
+* Add `db` to list of folders included by `rake notes` and `rake notes:custom`. *Antonio Cangiano*
+
* Engines with a dummy app include the rake tasks of dependencies in the app namespace.
Fix #8229
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index ae3993fbd8..bf3a26d400 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -123,6 +123,7 @@ module Rails
# Currently stores:
#
# * "action_dispatch.parameter_filter" => config.filter_parameters
+ # * "action_dispatch.redirect_filter" => config.filter_redirect
# * "action_dispatch.secret_token" => config.secret_token,
# * "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions
# * "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local
@@ -149,6 +150,7 @@ module Rails
super.merge({
"action_dispatch.parameter_filter" => config.filter_parameters,
+ "action_dispatch.redirect_filter" => config.filter_redirect,
"action_dispatch.secret_token" => config.secret_token,
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 89afeaeec5..f15fc9296d 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -13,7 +13,7 @@ module Rails
:railties_order, :relative_url_root, :secret_key_base, :secret_token,
:serve_static_assets, :ssl_options, :static_cache_control, :session_options,
:time_zone, :reload_classes_only_on_change,
- :queue, :queue_consumer, :beginning_of_week
+ :queue, :queue_consumer, :beginning_of_week, :filter_redirect
attr_writer :log_level
attr_reader :encoding
@@ -23,6 +23,7 @@ module Rails
self.encoding = "utf-8"
@consider_all_requests_local = false
@filter_parameters = []
+ @filter_redirect = []
@helpers_paths = []
@serve_static_assets = true
@static_cache_control = nil
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index b0fae13192..3ab2a3809e 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -60,7 +60,7 @@ when 'console'
require 'rails/commands/console'
options = Rails::Console.parse_arguments(ARGV)
- # RAILS_ENV needs to be set before config/application is required
+ # RAILS_ENV needs to be set before config/application is required
ENV['RAILS_ENV'] = options[:environment] if options[:environment]
# shift ARGV so IRB doesn't freak
diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb
index 92cee6b638..aef7600fbd 100644
--- a/railties/lib/rails/commands/console.rb
+++ b/railties/lib/rails/commands/console.rb
@@ -45,7 +45,7 @@ module Rails
end
def environment
- options[:environment] ||= ENV['RAILS_ENV'] || 'development'
+ options[:environment] ||= ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
end
def environment?
diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb
index 3f6966b4f0..315bcccf61 100644
--- a/railties/lib/rails/commands/profiler.rb
+++ b/railties/lib/rails/commands/profiler.rb
@@ -8,7 +8,7 @@ def options
defaults = ActiveSupport::Testing::Performance::DEFAULTS
OptionParser.new do |opt|
- opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]"
+ opt.banner = "Usage: rails profiler 'Ruby.code' 'Ruby.more_code' ... [OPTS]"
opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r }
opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o }
opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) }
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
index 62d82cc005..6adbdc6e0b 100644
--- a/railties/lib/rails/commands/runner.rb
+++ b/railties/lib/rails/commands/runner.rb
@@ -1,7 +1,7 @@
require 'optparse'
require 'rbconfig'
-options = { environment: (ENV['RAILS_ENV'] || "development").dup }
+options = { environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup }
code_or_file = nil
if ARGV.first.nil?
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index 0b897d736d..cdb29a8156 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -108,7 +108,7 @@ module Rails
super.merge({
Port: 3000,
DoNotReverseLookup: true,
- environment: (ENV['RAILS_ENV'] || "development").dup,
+ environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup,
daemonize: false,
debugger: false,
pid: File.expand_path("tmp/pids/server.pid"),
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
index e0539aa8bb..d87c7b7268 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -2,8 +2,8 @@
<html>
<head>
<title><%= camelized %></title>
- <%%= stylesheet_link_tag "application", media: "all" %>
- <%%= javascript_include_tag "application" %>
+ <%%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
+ <%%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%%= csrf_meta_tags %>
</head>
<body>
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore
index 8910bf5a06..52abb32479 100644
--- a/railties/lib/rails/generators/rails/app/templates/gitignore
+++ b/railties/lib/rails/generators/rails/app/templates/gitignore
@@ -2,7 +2,7 @@
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
-# git config --global core.excludesfile ~/.gitignore_global
+# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config
/.bundle
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
index b4f466fbd8..60d202c5ef 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -2,7 +2,7 @@ require 'rails/generators/rails/resource/resource_generator'
module Rails
module Generators
- class ScaffoldGenerator < ResourceGenerator # :nodoc:
+ class ScaffoldGenerator < ResourceGenerator # :nodoc:
remove_hook_for :resource_controller
remove_class_option :actions
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
index d6bce40b0c..24d97db407 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -4,6 +4,8 @@ require_dependency "<%= namespaced_file_path %>/application_controller"
<% end -%>
<% module_namespacing do -%>
class <%= controller_class_name %>Controller < ApplicationController
+ before_action :set_<%= singular_table_name %>, only: [ :show, :edit, :update, :destroy ]
+
# GET <%= route_url %>
# GET <%= route_url %>.json
def index
@@ -18,8 +20,6 @@ class <%= controller_class_name %>Controller < ApplicationController
# GET <%= route_url %>/1
# GET <%= route_url %>/1.json
def show
- @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
-
respond_to do |format|
format.html # show.html.erb
format.json { render json: <%= "@#{singular_table_name}" %> }
@@ -39,7 +39,6 @@ class <%= controller_class_name %>Controller < ApplicationController
# GET <%= route_url %>/1/edit
def edit
- @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
end
# POST <%= route_url %>
@@ -61,8 +60,6 @@ class <%= controller_class_name %>Controller < ApplicationController
# PATCH/PUT <%= route_url %>/1
# PATCH/PUT <%= route_url %>/1.json
def update
- @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
-
respond_to do |format|
if @<%= orm_instance.update_attributes("#{singular_table_name}_params") %>
format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> }
@@ -77,7 +74,6 @@ class <%= controller_class_name %>Controller < ApplicationController
# DELETE <%= route_url %>/1
# DELETE <%= route_url %>/1.json
def destroy
- @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
@<%= orm_instance.destroy %>
respond_to do |format|
@@ -87,6 +83,10 @@ class <%= controller_class_name %>Controller < ApplicationController
end
private
+ # Use callbacks to share common setup or constraints between actions.
+ def set_<%= singular_table_name %>
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
+ end
# Use this method to whitelist the permissible parameters. Example: params.require(:person).permit(:name, :age)
# Also, you can specialize this method with per-user checking of permissible attributes.
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 3474b02af4..ac806e8006 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -15,7 +15,7 @@
class SourceAnnotationExtractor
class Annotation < Struct.new(:line, :tag, :text)
def self.directories
- @@directories ||= %w(app config lib script test) + (ENV['SOURCE_ANNOTATION_DIRECTORIES'] || '').split(',')
+ @@directories ||= %w(app config db lib script test) + (ENV['SOURCE_ANNOTATION_DIRECTORIES'] || '').split(',')
end
# Returns a representation of the annotation that looks like this:
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
index 2ea1d2aff4..ecd5e03978 100644
--- a/railties/test/abstract_unit.rb
+++ b/railties/test/abstract_unit.rb
@@ -7,7 +7,6 @@ require 'minitest/autorun'
require 'fileutils'
require 'active_support'
-
require 'action_controller'
require 'rails/all'
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 03798d572a..ccb47663d4 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -55,8 +55,8 @@ module ApplicationTests
def db_migrate_and_status
Dir.chdir(app_path) do
- `rails generate model book title:string`
- `bundle exec rake db:migrate`
+ `rails generate model book title:string;
+ bundle exec rake db:migrate`
output = `bundle exec rake db:migrate:status`
assert_match(/database:\s+\S+#{expected[:database]}/, output)
assert_match(/up\s+\d{14}\s+Create books/, output)
@@ -78,9 +78,8 @@ module ApplicationTests
def db_schema_dump
Dir.chdir(app_path) do
- `rails generate model book title:string`
- `rake db:migrate`
- `rake db:schema:dump`
+ `rails generate model book title:string;
+ rake db:migrate db:schema:dump`
schema_dump = File.read("db/schema.rb")
assert_match(/create_table \"books\"/, schema_dump)
end
@@ -97,9 +96,8 @@ module ApplicationTests
def db_fixtures_load
Dir.chdir(app_path) do
- `rails generate model book title:string`
- `bundle exec rake db:migrate`
- `bundle exec rake db:fixtures:load`
+ `rails generate model book title:string;
+ bundle exec rake db:migrate db:fixtures:load`
assert_match(/#{expected[:database]}/,
ActiveRecord::Base.connection_config[:database])
require "#{app_path}/app/models/book"
@@ -122,13 +120,11 @@ module ApplicationTests
def db_structure_dump_and_load
Dir.chdir(app_path) do
- `rails generate model book title:string`
- `bundle exec rake db:migrate`
- `bundle exec rake db:structure:dump`
+ `rails generate model book title:string;
+ bundle exec rake db:migrate db:structure:dump`
structure_dump = File.read("db/structure.sql")
assert_match(/CREATE TABLE \"books\"/, structure_dump)
- `bundle exec rake db:drop`
- `bundle exec rake db:structure:load`
+ `bundle exec rake db:drop db:structure:load`
assert_match(/#{expected[:database]}/,
ActiveRecord::Base.connection_config[:database])
require "#{app_path}/app/models/book"
@@ -152,10 +148,8 @@ module ApplicationTests
def db_test_load_structure
Dir.chdir(app_path) do
- `rails generate model book title:string`
- `bundle exec rake db:migrate`
- `bundle exec rake db:structure:dump`
- `bundle exec rake db:test:load_structure`
+ `rails generate model book title:string;
+ bundle exec rake db:migrate db:structure:dump db:test:load_structure`
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Base.establish_connection 'test'
require "#{app_path}/app/models/book"
@@ -178,4 +172,4 @@ module ApplicationTests
end
end
end
-end \ No newline at end of file
+end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 7a227098ba..744bb93671 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -60,6 +60,7 @@ module ApplicationTests
test 'notes finds notes in default directories' do
app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
+ app_file "db/some_seeds.rb", "# TODO: note in db directory"
app_file "lib/some_file.rb", "# TODO: note in lib directory"
app_file "script/run_something.rb", "# TODO: note in script directory"
app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
@@ -80,12 +81,13 @@ module ApplicationTests
assert_match(/note in app directory/, output)
assert_match(/note in config directory/, output)
+ assert_match(/note in db directory/, output)
assert_match(/note in lib directory/, output)
assert_match(/note in script directory/, output)
assert_match(/note in test directory/, output)
assert_no_match(/note in some_other directory/, output)
- assert_equal 5, lines.size
+ assert_equal 6, lines.size
lines.each do |line_number|
assert_equal 4, line_number.size
@@ -96,6 +98,7 @@ module ApplicationTests
test 'notes finds notes in custom directories' do
app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
+ app_file "db/some_seeds.rb", "# TODO: note in db directory"
app_file "lib/some_file.rb", "# TODO: note in lib directory"
app_file "script/run_something.rb", "# TODO: note in script directory"
app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
@@ -116,13 +119,14 @@ module ApplicationTests
assert_match(/note in app directory/, output)
assert_match(/note in config directory/, output)
+ assert_match(/note in db directory/, output)
assert_match(/note in lib directory/, output)
assert_match(/note in script directory/, output)
assert_match(/note in test directory/, output)
assert_match(/note in some_other directory/, output)
- assert_equal 6, lines.size
+ assert_equal 7, lines.size
lines.each do |line_number|
assert_equal 4, line_number.size
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index 81ed5873a5..f65b5e2f2d 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -1,8 +1,10 @@
require 'isolation/abstract_unit'
+require 'env_helpers'
module ApplicationTests
class RunnerTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
+ include EnvHelpers
def setup
build_app
@@ -67,5 +69,21 @@ module ApplicationTests
assert_match "true", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.application.config.ran"` }
end
+
+ def test_default_environment
+ assert_match "development", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.env"` }
+ end
+
+ def test_environment_with_rails_env
+ with_rails_env "production" do
+ assert_match "production", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.env"` }
+ end
+ end
+
+ def test_environment_with_rack_env
+ with_rack_env "production" do
+ assert_match "production", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.env"` }
+ end
+ end
end
end
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index f99ea13022..9e449856f4 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -1,14 +1,14 @@
require 'abstract_unit'
+require 'env_helpers'
require 'rails/commands/console'
class Rails::ConsoleTest < ActiveSupport::TestCase
+ include EnvHelpers
+
class FakeConsole
def self.start; end
end
- def setup
- end
-
def test_sandbox_option
console = Rails::Console.new(app, parse_arguments(["--sandbox"]))
assert console.sandbox?
@@ -78,7 +78,14 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
assert_match(/\sspecial-production\s/, output)
end
end
-
+
+ def test_default_environment_with_rack_env
+ with_rack_env 'production' do
+ start
+ assert_match(/\sproduction\s/, output)
+ end
+ end
+
def test_e_option
start ['-e', 'special-production']
assert_match(/\sspecial-production\s/, output)
@@ -126,12 +133,4 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
def parse_arguments(args)
Rails::Console.parse_arguments(args)
end
-
- def with_rails_env(env)
- original_rails_env = ENV['RAILS_ENV']
- ENV['RAILS_ENV'] = env
- yield
- ensure
- ENV['RAILS_ENV'] = original_rails_env
- end
end
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index 4a3ea82e3d..cb57b3c0cd 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -1,7 +1,9 @@
require 'abstract_unit'
+require 'env_helpers'
require 'rails/commands/server'
class Rails::ServerTest < ActiveSupport::TestCase
+ include EnvHelpers
def test_environment_with_server_option
args = ["thin", "-e", "production"]
@@ -23,4 +25,18 @@ class Rails::ServerTest < ActiveSupport::TestCase
assert_nil options[:environment]
assert_equal 'thin', options[:server]
end
+
+ def test_environment_with_rails_env
+ with_rails_env 'production' do
+ server = Rails::Server.new
+ assert_equal 'production', server.options[:environment]
+ end
+ end
+
+ def test_environment_with_rack_env
+ with_rack_env 'production' do
+ server = Rails::Server.new
+ assert_equal 'production', server.options[:environment]
+ end
+ end
end
diff --git a/railties/test/env_helpers.rb b/railties/test/env_helpers.rb
new file mode 100644
index 0000000000..6223c85bbf
--- /dev/null
+++ b/railties/test/env_helpers.rb
@@ -0,0 +1,26 @@
+module EnvHelpers
+ private
+
+ def with_rails_env(env)
+ switch_env 'RAILS_ENV', env do
+ switch_env 'RACK_ENV', nil do
+ yield
+ end
+ end
+ end
+
+ def with_rack_env(env)
+ switch_env 'RACK_ENV', env do
+ switch_env 'RAILS_ENV', nil do
+ yield
+ end
+ end
+ end
+
+ def switch_env(key, value)
+ old, ENV[key] = ENV[key], value
+ yield
+ ensure
+ ENV[key] = old
+ end
+end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 8af92479c3..54734ed260 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -1,8 +1,11 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/app/app_generator'
+require 'env_helpers'
class ActionsTest < Rails::Generators::TestCase
include GeneratorsTestHelper
+ include EnvHelpers
+
tests Rails::Generators::AppGenerator
arguments [destination_root]
@@ -154,10 +157,9 @@ class ActionsTest < Rails::Generators::TestCase
def test_rake_should_run_rake_command_with_default_env
generator.expects(:run).once.with("rake log:clear RAILS_ENV=development", verbose: false)
- old_env, ENV['RAILS_ENV'] = ENV["RAILS_ENV"], nil
- action :rake, 'log:clear'
- ensure
- ENV["RAILS_ENV"] = old_env
+ with_rails_env nil do
+ action :rake, 'log:clear'
+ end
end
def test_rake_with_env_option_should_run_rake_command_in_env
@@ -167,26 +169,23 @@ class ActionsTest < Rails::Generators::TestCase
def test_rake_with_rails_env_variable_should_run_rake_command_in_env
generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
- old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production"
- action :rake, 'log:clear'
- ensure
- ENV["RAILS_ENV"] = old_env
+ with_rails_env "production" do
+ action :rake, 'log:clear'
+ end
end
def test_env_option_should_win_over_rails_env_variable_when_running_rake
generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
- old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "staging"
- action :rake, 'log:clear', env: 'production'
- ensure
- ENV["RAILS_ENV"] = old_env
+ with_rails_env "staging" do
+ action :rake, 'log:clear', env: 'production'
+ end
end
def test_rake_with_sudo_option_should_run_rake_command_with_sudo
generator.expects(:run).once.with("sudo rake log:clear RAILS_ENV=development", verbose: false)
- old_env, ENV['RAILS_ENV'] = ENV["RAILS_ENV"], nil
- action :rake, 'log:clear', sudo: true
- ensure
- ENV["RAILS_ENV"] = old_env
+ with_rails_env nil do
+ action :rake, 'log:clear', sudo: true
+ end
end
def test_capify_should_run_the_capify_command
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 54d5a9db6f..7fcc0a7409 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -30,17 +30,13 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_match(/@product_lines = ProductLine\.all/, m)
end
- assert_instance_method :show, content do |m|
- assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
- end
+ assert_instance_method :show, content
assert_instance_method :new, content do |m|
assert_match(/@product_line = ProductLine\.new/, m)
end
- assert_instance_method :edit, content do |m|
- assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
- end
+ assert_instance_method :edit, content
assert_instance_method :create, content do |m|
assert_match(/@product_line = ProductLine\.new\(product_line_params\)/, m)
@@ -49,15 +45,17 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
assert_instance_method :update, content do |m|
- assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
assert_match(/@product_line\.update_attributes\(product_line_params\)/, m)
assert_match(/@product_line\.errors/, m)
end
assert_instance_method :destroy, content do |m|
- assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
assert_match(/@product_line\.destroy/, m)
end
+
+ assert_instance_method :set_product_line, content do |m|
+ assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
+ end
end
assert_file "test/controllers/product_lines_controller_test.rb" do |test|
@@ -149,17 +147,13 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_match(/@admin_roles = Admin::Role\.all/, m)
end
- assert_instance_method :show, content do |m|
- assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m)
- end
+ assert_instance_method :show, content
assert_instance_method :new, content do |m|
assert_match(/@admin_role = Admin::Role\.new/, m)
end
- assert_instance_method :edit, content do |m|
- assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m)
- end
+ assert_instance_method :edit, content
assert_instance_method :create, content do |m|
assert_match(/@admin_role = Admin::Role\.new\(admin_role_params\)/, m)
@@ -168,15 +162,17 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
assert_instance_method :update, content do |m|
- assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m)
assert_match(/@admin_role\.update_attributes\(admin_role_params\)/, m)
assert_match(/@admin_role\.errors/, m)
end
assert_instance_method :destroy, content do |m|
- assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m)
assert_match(/@admin_role\.destroy/, m)
end
+
+ assert_instance_method :set_admin_role, content do |m|
+ assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m)
+ end
end
assert_file "test/controllers/admin/roles_controller_test.rb",