aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile17
-rw-r--r--RAILS_VERSION2
-rw-r--r--actionmailer/CHANGELOG.md3
-rw-r--r--actionmailer/lib/action_mailer/version.rb2
-rw-r--r--actionpack/CHANGELOG.md175
-rw-r--r--actionpack/actionpack.gemspec4
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb37
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb7
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb3
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb11
-rw-r--r--actionpack/lib/action_dispatch/journey/path/pattern.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb17
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb26
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb7
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb55
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb67
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view/dependency_tracker.rb93
-rw-r--r--actionpack/lib/action_view/digestor.rb51
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb4
-rw-r--r--actionpack/test/controller/mime_responds_test.rb21
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb3
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb35
-rw-r--r--actionpack/test/controller/routing_test.rb16
-rw-r--r--actionpack/test/controller/webservice_test.rb146
-rw-r--r--actionpack/test/dispatch/cookies_test.rb21
-rw-r--r--actionpack/test/dispatch/request/xml_params_parsing_test.rb182
-rw-r--r--actionpack/test/dispatch/request_test.rb3
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb85
-rw-r--r--actionpack/test/dispatch/routing_test.rb97
-rw-r--r--actionpack/test/fixtures/respond_to/using_defaults_with_all.html.erb1
-rw-r--r--actionpack/test/journey/path/pattern_test.rb4
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb3
-rw-r--r--actionpack/test/template/date_helper_test.rb36
-rw-r--r--actionpack/test/template/dependency_tracker_test.rb46
-rw-r--r--actionpack/test/template/digestor_test.rb3
-rw-r--r--actionpack/test/template/form_options_helper_test.rb19
-rw-r--r--actionpack/test/template/number_helper_test.rb348
-rw-r--r--activemodel/CHANGELOG.md53
-rw-r--r--activemodel/lib/active_model/validator.rb3
-rw-r--r--activemodel/lib/active_model/version.rb2
-rw-r--r--activemodel/test/cases/errors_test.rb7
-rw-r--r--activerecord/CHANGELOG.md257
-rw-r--r--activerecord/RUNNING_UNIT_TESTS.rdoc2
-rw-r--r--activerecord/activerecord.gemspec2
-rw-r--r--activerecord/lib/active_record/associations.rb4
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_part.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb7
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb2
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb38
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb32
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb32
-rw-r--r--activerecord/lib/active_record/core.rb50
-rw-r--r--activerecord/lib/active_record/explain.rb53
-rw-r--r--activerecord/lib/active_record/integration.rb6
-rw-r--r--activerecord/lib/active_record/persistence.rb26
-rw-r--r--activerecord/lib/active_record/querying.rb24
-rw-r--r--activerecord/lib/active_record/railtie.rb26
-rw-r--r--activerecord/lib/active_record/relation.rb12
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb6
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb7
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb84
-rw-r--r--activerecord/lib/active_record/store.rb33
-rw-r--r--activerecord/lib/active_record/transactions.rb36
-rw-r--r--activerecord/lib/active_record/version.rb2
-rw-r--r--activerecord/test/cases/adapter_test.rb14
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb6
-rw-r--r--activerecord/test/cases/associations/eager_test.rb18
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb10
-rw-r--r--activerecord/test/cases/base_test.rb34
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb51
-rw-r--r--activerecord/test/cases/dup_test.rb9
-rw-r--r--activerecord/test/cases/explain_test.rb72
-rw-r--r--activerecord/test/cases/migration/columns_test.rb (renamed from activerecord/test/cases/migration/rename_column_test.rb)62
-rw-r--r--activerecord/test/cases/migration/index_test.rb28
-rw-r--r--activerecord/test/cases/migration/rename_table_test.rb12
-rw-r--r--activerecord/test/cases/persistence_test.rb8
-rw-r--r--activerecord/test/cases/quoting_test.rb14
-rw-r--r--activerecord/test/cases/relation/where_test.rb25
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb6
-rw-r--r--activerecord/test/cases/relation_test.rb17
-rw-r--r--activerecord/test/cases/relations_test.rb16
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb12
-rw-r--r--activerecord/test/cases/store_test.rb12
-rw-r--r--activerecord/test/cases/timestamp_test.rb12
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb62
-rw-r--r--activerecord/test/cases/transactions_test.rb4
-rw-r--r--activerecord/test/models/admin/user.rb9
-rw-r--r--activerecord/test/models/speedometer.rb2
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb6
-rw-r--r--activerecord/test/schema/schema.rb2
-rw-r--r--activesupport/CHANGELOG.md178
-rw-r--r--activesupport/README.rdoc4
-rw-r--r--activesupport/activesupport.gemspec4
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb15
-rw-r--r--activesupport/lib/active_support/gzip.rb4
-rw-r--r--activesupport/lib/active_support/i18n.rb3
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb2
-rw-r--r--activesupport/lib/active_support/number_helper.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb38
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb4
-rw-r--r--activesupport/lib/active_support/values/unicode_tables.datbin904408 -> 904483 bytes
-rw-r--r--activesupport/lib/active_support/version.rb2
-rw-r--r--activesupport/test/caching_test.rb12
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb11
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb25
-rw-r--r--activesupport/test/gzip_test.rb18
-rw-r--r--activesupport/test/number_helper_test.rb7
-rw-r--r--guides/CHANGELOG.md3
-rw-r--r--guides/code/getting_started/config/environments/development.rb4
-rw-r--r--guides/code/getting_started/config/environments/production.rb4
-rw-r--r--guides/code/getting_started/config/routes.rb4
-rw-r--r--guides/source/4_0_release_notes.md4
-rw-r--r--guides/source/action_controller_overview.md8
-rw-r--r--guides/source/action_mailer_basics.md5
-rw-r--r--guides/source/active_record_callbacks.md16
-rw-r--r--guides/source/active_record_querying.md43
-rw-r--r--guides/source/active_record_validations.md29
-rw-r--r--guides/source/active_support_core_extensions.md20
-rw-r--r--guides/source/caching_with_rails.md5
-rw-r--r--guides/source/configuring.md2
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md2
-rw-r--r--guides/source/engines.md18
-rw-r--r--guides/source/generators.md2
-rw-r--r--guides/source/getting_started.md4
-rw-r--r--guides/source/kindle/KINDLE.md2
-rw-r--r--guides/source/layouts_and_rendering.md3
-rw-r--r--guides/source/migrations.md12
-rw-r--r--guides/source/routing.md12
-rw-r--r--guides/source/testing.md5
-rw-r--r--guides/source/upgrading_ruby_on_rails.md33
-rw-r--r--guides/source/working_with_javascript_in_rails.md2
-rw-r--r--install.rb6
-rw-r--r--rails.gemspec4
-rw-r--r--railties/CHANGELOG.md44
-rw-r--r--railties/lib/rails/app_rails_loader.rb24
-rw-r--r--railties/lib/rails/application/configuration.rb14
-rw-r--r--railties/lib/rails/application/finisher.rb7
-rw-r--r--railties/lib/rails/cli.rb3
-rw-r--r--railties/lib/rails/code_statistics.rb68
-rw-r--r--railties/lib/rails/code_statistics_calculator.rb79
-rw-r--r--railties/lib/rails/engine.rb12
-rw-r--r--railties/lib/rails/engine/configuration.rb24
-rw-r--r--railties/lib/rails/generators/actions.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb21
-rw-r--r--railties/lib/rails/generators/migration.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml10
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb12
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb4
-rw-r--r--railties/lib/rails/paths.rb44
-rw-r--r--railties/lib/rails/ruby_version_check.rb4
-rw-r--r--railties/lib/rails/tasks/documentation.rake2
-rw-r--r--railties/lib/rails/version.rb2
-rw-r--r--railties/test/app_rails_loader_test.rb65
-rw-r--r--railties/test/application/initializers/boot_test.rb20
-rw-r--r--railties/test/application/initializers/load_path_test.rb2
-rw-r--r--railties/test/application/paths_test.rb10
-rw-r--r--railties/test/application/rake/dbs_test.rb15
-rw-r--r--railties/test/application/rake_test.rb20
-rw-r--r--railties/test/code_statistics_calculator_test.rb288
-rw-r--r--railties/test/fixtures/lib/app_builders/empty_builder.rb2
-rw-r--r--railties/test/fixtures/lib/app_builders/simple_builder.rb7
-rw-r--r--railties/test/fixtures/lib/app_builders/tweak_builder.rb7
-rw-r--r--railties/test/fixtures/lib/plugin_builders/empty_builder.rb2
-rw-r--r--railties/test/fixtures/lib/plugin_builders/simple_builder.rb7
-rw-r--r--railties/test/fixtures/lib/plugin_builders/spec_builder.rb19
-rw-r--r--railties/test/fixtures/lib/plugin_builders/tweak_builder.rb7
-rw-r--r--railties/test/generators/app_generator_test.rb34
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb35
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb1
-rw-r--r--railties/test/generators/shared_generator_tests.rb62
-rw-r--r--railties/test/paths_test.rb56
-rw-r--r--version.rb2
210 files changed, 2863 insertions, 2006 deletions
diff --git a/Gemfile b/Gemfile
index 09d0f502ce..1e3d746a9b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,27 +2,17 @@ source 'https://rubygems.org'
gemspec
-gem 'arel', github: 'rails/arel', branch: 'master'
-
gem 'mocha', '~> 0.13.0', require: false
-gem 'rack-test', github: 'brynary/rack-test'
gem 'rack-cache', '~> 1.2'
gem 'bcrypt-ruby', '~> 3.0.0'
-gem 'jquery-rails', '~> 2.2.0', github: 'rails/jquery-rails'
+gem 'jquery-rails', '~> 2.2.0'
gem 'turbolinks'
-gem 'coffee-rails', github: 'rails/coffee-rails'
-
-gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders', branch: 'master'
-
-# Needed for compiling the ActionDispatch::Journey parser
-gem 'racc', '>=1.4.6', require: false
+gem 'coffee-rails', '~> 4.0.0.beta1'
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
gem 'uglifier', require: false
-gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
-
group :doc do
gem 'sdoc', github: 'voloko/sdoc'
gem 'redcarpet', '~> 2.2.2', platforms: :ruby
@@ -48,6 +38,9 @@ platforms :ruby do
gem 'yajl-ruby'
gem 'nokogiri', '>= 1.4.5'
+ # Needed for compiling the ActionDispatch::Journey parser
+ gem 'racc', '>=1.4.6', require: false
+
# AR
gem 'sqlite3', '~> 1.3.6'
diff --git a/RAILS_VERSION b/RAILS_VERSION
index e1e048d8f0..14e1b44259 100644
--- a/RAILS_VERSION
+++ b/RAILS_VERSION
@@ -1 +1 @@
-4.0.0.beta
+4.0.0.beta1
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 7e57fb39f3..8f74ac0928 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,5 +1,8 @@
## Rails 4.0.0 (unreleased) ##
+
+## Rails 4.0.0.beta1 (February 25, 2013) ##
+
* Allow passing interpolations to `#default_i18n_subject`, e.g.:
# config/locales/en.yml
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index 0cb5dc6d64..997046b971 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -3,7 +3,7 @@ module ActionMailer
MAJOR = 4
MINOR = 0
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index d6a2687037..2a9afed35b 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,21 +1,99 @@
## Rails 4.0.0 (unreleased) ##
-* We don't support the `:controller` option for route definitions
- with the ruby constant notation. This will now result in an
- `ArgumentError`.
+* Skip valid encoding checks for non-String parameters that come
+ from the matched route's defaults.
+ Fixes #9435.
Example:
- # This raises an ArgumentError:
- resources :posts, :controller => "Admin::Posts"
- # Use directory notation instead:
- resources :posts, :controller => "admin/posts"
+ root to: 'main#posts', page: 1
+
+ *Yves Senn*
+
+* Don't verify Regexp requirements for non-Regexp `:constraints`.
+ Fixes #9432.
+
+ Example:
+
+ get '/photos.:format' => 'feeds#photos', constraints: {format: 'xml'}
+
+ *Yves Senn*
+
+* Make `ActionDispatch::Journey::Path::Pattern#new` raise more meaningful exception message.
+
+ *Thierry Zires*
+
+## Rails 4.0.0.beta1 (February 25, 2013) ##
+
+* Fix `respond_to` not using formats that have no block if all is present. *Michael Grosser*
+
+* New applications use an encrypted session store by default.
+
+ *Santiago Pastorino*
+
+* Determine the controller#action from only the matched path when using the
+ shorthand syntax. Previously the complete path was used, which led
+ to problems with nesting (scopes and namespaces).
+ Fixes #7554.
+
+ Example:
+
+ # This will route to questions#new.
+ scope ':locale' do
+ get 'questions/new'
+ end
+
+ *Yves Senn*
+
+* Remove support for parsing XML parameters from request. If you still want to parse XML
+ parameters, please install `actionpack-xml_parser' gem.
+
+ *Prem Sichanugrist*
+
+* Remove support for parsing YAML parameters from request.
+
+ *Aaron Patterson*
+
+* Add a message when you have no routes defined to both `rake routes` and
+ GET "/rails/info/routes" that lets you know you have none defined and links
+ to the Rails guide on the topic.
+
+ *Steve Klabnik*
+
+* Change `image_alt` method to replace underscores/hyphens to spaces in filenames.
+
+ Previously, underscored filenames became `alt="A_long_file_name_with_underscores"`
+ in HTML, which is poor for accessibility. For instance, Apple's VoiceOver Utility
+ pronounces each underscore. `A_long_file_name` thus would be read as `A underscore
+ long underscore file underscore name.` Now underscored or hyphenated filenames
+ (both of which are very popular naming conventions) read more naturally in
+ screen readers by converting both hyphens and underscores to spaces.
+
+ Before:
+
+ image_tag('underscored_file_name.png')
+ # => <img alt="Underscored_file_name" src="/assets/underscored_file_name.png" />
+
+ After:
+
+ image_tag('underscored_file_name.png')
+ # => <img alt="Underscored file name" src="/assets/underscored_file_name.png" />
+
+ *Nick Cox*
+
+* We don't support Ruby constant notation in the `:controller` option for route
+ definitions. So, this raises an `ArgumentError` now:
+
+ resources :posts, controller: "Admin::Posts" # WRONG
+
+ Use path notation instead:
+
+ resources :posts, controller: "admin/posts" # RIGHT
*Yves Senn*
* `assert_template` can be used to verify the locals of partials,
which live inside a directory.
- Fixes #8516.
# Prefixed partials inside directories worked and still work.
assert_template partial: 'directory/_partial', locals: {name: 'John'}
@@ -23,23 +101,25 @@
# This did not work but does now.
assert_template partial: 'directory/partial', locals: {name: 'John'}
+ Fixes #8516.
+
*Yves Senn*
-* Fix `content_tag_for` with array html option.
+* Fix `content_tag_for` with array HTML option.
It would embed array as string instead of joining it like `content_tag` does:
content_tag(:td, class: ["foo", "bar"]){}
- #=> '<td class="foo bar"></td>'
+ # => <td class="foo bar"></td>
Before:
content_tag_for(:td, item, class: ["foo", "bar"])
- #=> '<td class="item [&quot;foo&quot;, &quot;bar&quot;]" id="item_1"></td>'
+ # => <td class="item [&quot;foo&quot;, &quot;bar&quot;]" id="item_1"></td>
After:
content_tag_for(:td, item, class: ["foo", "bar"])
- #=> '<td class="item foo bar" id="item_1"></td>'
+ # => <td class="item foo bar" id="item_1"></td>
*Semyon Perepelitsa*
@@ -59,35 +139,18 @@
*Piotr Sarnacki*
-* Add javascript based routing path matcher to `/rails/info/routes`.
+* Add JavaScript based routing path matcher to `/rails/info/routes`.
Routes can now be filtered by whether or not they match a path.
*Richard Schneeman*
-* Given
-
- params.permit(:name)
-
- `:name` passes if it is a key of `params` whose value is a permitted scalar.
-
- Similarly, given
-
- params.permit(tags: [])
-
- `:tags` passes if it is a key of `params` whose value is an array of
- permitted scalars.
-
- Permitted scalars filtering happens at any level of nesting.
-
- *Xavier Noria*
-
* Change the behavior of route defaults so that explicit defaults are no longer
required where the key is not part of the path. For example:
resources :posts, bucket_type: 'posts'
will be required whenever constructing the url from a hash such as a functional
- test or using url_for directly. However using the explicit form alters the
+ test or using `url_for` directly. However using the explicit form alters the
behavior so it's not required:
resources :projects, defaults: { bucket_type: 'projects' }
@@ -121,7 +184,7 @@
*Colin Burn-Murdoch*
-* Fixed json params parsing regression for non-object JSON content.
+* Fixed JSON params parsing regression for non-object JSON content.
*Dylan Smith*
@@ -159,12 +222,13 @@
* Do not append second slash to `root_url` when using `trailing_slash: true`
Fix #8700
- Example:
- # before
- root_url # => http://test.host//
+ Before:
- # after
- root_url # => http://test.host/
+ root_url(trailing_slash: true) # => http://test.host//
+
+ After:
+
+ root_url(trailing_slash: true) # => http://test.host/
*Yves Senn*
@@ -188,8 +252,8 @@
*Yves Senn*
-* Added `Mime::NullType` class. This allows to use html?, xml?, json?..etc when
- the `format` of `request` is unknown, without raise an exception.
+* Added `Mime::NullType` class. This allows to use `html?`, `xml?`, `json?`, etc.
+ when the format of the request is unknown, without raising an exception.
*Angelo Capilleri*
@@ -214,7 +278,7 @@
*Matt Venables*
-* Prevent raising EOFError on multipart GET request (IE issue). *Adam Stankiewicz*
+* Prevent raising `EOFError` on multipart GET request (IE issue). *Adam Stankiewicz*
* 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,
@@ -260,7 +324,7 @@
*Stephen Ausman + Fabrizio Regini + Angelo Capilleri*
-* Add filter capability to ActionController logs for redirect locations:
+* Add logging filter capability for redirect URLs:
config.filter_redirect << 'http://please.hide.it/'
@@ -359,23 +423,17 @@
Before:
check_box("post", "comment_ids", { multiple: true, index: "foo" }, 1)
- #=> <input name=\"post[foo][comment_ids]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids]\" type=\"checkbox\" value=\"1\" />
+ # => <input name=\"post[foo][comment_ids]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids]\" type=\"checkbox\" value=\"1\" />
After:
check_box("post", "comment_ids", { multiple: true, index: "foo" }, 1)
- #=> <input name=\"post[foo][comment_ids][]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids][]\" type=\"checkbox\" value=\"1\" />
+ # => <input name=\"post[foo][comment_ids][]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids][]\" type=\"checkbox\" value=\"1\" />
Fix #8108.
*Daniel Fox, Grant Hutchins & Trace Wax*
-* `BestStandardsSupport` middleware now appends it's `X-UA-Compatible` value to app's
- returned value if any.
- Fix #8086.
-
- *Nikita Afanasenko*
-
* `date_select` helper accepts `with_css_classes: true` to add css classes similar with type
of generated select tags.
@@ -428,10 +486,6 @@
* Failsafe exception returns `text/plain`. *Steve Klabnik*
-* Remove `rack-cache` dependency from Action Pack and declare it on Gemfile
-
- *Guillermo Iguaran*
-
* Rename internal variables on `ActionController::TemplateAssertions` to prevent
naming collisions. `@partials`, `@templates` and `@layouts` are now prefixed with an underscore.
Fix #7459.
@@ -517,9 +571,7 @@
*Guillermo Iguaran*
* `ActionDispatch::Session::MemCacheStore` now uses `dalli` instead of the deprecated
- `memcache-client` gem. As side effect the autoloading of unloaded classes objects
- saved as values in session isn't supported anymore when mem_cache session store is
- used, this can have an impact in apps only when config.cache_classes is false.
+ `memcache-client` gem.
*Arun Agrawal + Guillermo Iguaran*
@@ -793,9 +845,9 @@
* `assert_generates`, `assert_recognizes`, and `assert_routing` all raise
`Assertion` instead of `RoutingError` *David Chelimsky*
-* URL path parameters with invalid encoding now raise ActionController::BadRequest. *Andrew White*
+* URL path parameters with invalid encoding now raise `ActionController::BadRequest`. *Andrew White*
-* Malformed query and request parameter hashes now raise ActionController::BadRequest. *Andrew White*
+* Malformed query and request parameter hashes now raise `ActionController::BadRequest`. *Andrew White*
* Add `divider` option to `grouped_options_for_select` to generate a separator
`optgroup` automatically, and deprecate `prompt` as third argument, in favor
@@ -822,7 +874,7 @@
*Andrew White*
-* `respond_to` and `respond_with` now raise ActionController::UnknownFormat instead
+* `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. *Steven Soroka*
@@ -852,7 +904,7 @@
* Remove the leading \n added by textarea on `assert_select`. *Santiago Pastorino*
* 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,
+ 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.
@@ -947,9 +999,6 @@
* `check_box` with `:form` html5 attribute will now replicate the `:form`
attribute to the hidden field as well. *Carlos Antonio da Silva*
-* Turn off verbose mode of rack-cache, we still have X-Rack-Cache to
- check that info. Closes #5245. *Santiago Pastorino*
-
* `label` form helper accepts `for: nil` to not generate the attribute. *Carlos Antonio da Silva*
* Add `:format` option to `number_to_percentage`. *Rodrigo Flores*
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 70a1975ee9..03eeb841ee 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -21,8 +21,8 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
s.add_dependency 'builder', '~> 3.1.0'
- s.add_dependency 'rack', '~> 1.5.0'
- s.add_dependency 'rack-test', '~> 0.6.1'
+ s.add_dependency 'rack', '~> 1.5.2'
+ s.add_dependency 'rack-test', '~> 0.6.2'
s.add_dependency 'erubis', '~> 2.7.0'
s.add_development_dependency 'activemodel', version
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 93568da9ef..834d44f045 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -420,7 +420,7 @@ module ActionController #:nodoc:
end
def response
- @responses[format] || @responses[Mime::ALL]
+ @responses.fetch(format, @responses[Mime::ALL])
end
def negotiate_format(request)
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 77b173979e..d275a854fd 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -50,6 +50,10 @@ module ActionController #:nodoc:
config_accessor :request_forgery_protection_token
self.request_forgery_protection_token ||= :authenticity_token
+ # Holds the class which implements the request forgery protection.
+ config_accessor :forgery_protection_strategy
+ self.forgery_protection_strategy = nil
+
# Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
config_accessor :allow_forgery_protection
self.allow_forgery_protection = true if allow_forgery_protection.nil?
@@ -82,14 +86,14 @@ module ActionController #:nodoc:
# * <tt>:reset_session</tt> - Resets the session.
# * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
def protect_from_forgery(options = {})
- include protection_method_module(options[:with] || :null_session)
+ self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
prepend_before_action :verify_authenticity_token, options
end
private
- def protection_method_module(name)
+ def protection_method_class(name)
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
rescue NameError
raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
@@ -97,17 +101,22 @@ module ActionController #:nodoc:
end
module ProtectionMethods
- module NullSession
- protected
+ class NullSession
+ def initialize(controller)
+ @controller = controller
+ end
# This is the method that defines the application behavior when a request is found to be unverified.
def handle_unverified_request
+ request = @controller.request
request.session = NullSessionHash.new(request.env)
request.env['action_dispatch.request.flash_hash'] = nil
request.env['rack.session.options'] = { skip: true }
request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
end
+ protected
+
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
def initialize(env)
super(nil, env)
@@ -126,7 +135,7 @@ module ActionController #:nodoc:
host = request.host
secure = request.ssl?
- new(key_generator, host, secure)
+ new(key_generator, host, secure, options_for_env({}))
end
def write(*)
@@ -135,16 +144,20 @@ module ActionController #:nodoc:
end
end
- module ResetSession
- protected
+ class ResetSession
+ def initialize(controller)
+ @controller = controller
+ end
def handle_unverified_request
- reset_session
+ @controller.reset_session
end
end
- module Exception
- protected
+ class Exception
+ def initialize(controller)
+ @controller = controller
+ end
def handle_unverified_request
raise ActionController::InvalidAuthenticityToken
@@ -153,6 +166,10 @@ module ActionController #:nodoc:
end
protected
+ def handle_unverified_request
+ forgery_protection_strategy.new(self).handle_unverified_request
+ end
+
# The actual before_action that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
unless verified_request?
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 7e720ca6f5..e4dcd3213f 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -191,9 +191,9 @@ module ActionController
#
# +:name+ passes it is a key of +params+ whose associated value is of type
# +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+,
- # +Date+, +Time+, +DateTime+, +StringIO+, +IO+, or
- # +ActionDispatch::Http::UploadedFile+. Otherwise, the key +:name+ is
- # filtered out.
+ # +Date+, +Time+, +DateTime+, +StringIO+, +IO+,
+ # +ActionDispatch::Http::UploadedFile+ or +Rack::Test::UploadedFile+.
+ # Otherwise, the key +:name+ is filtered out.
#
# You may declare that the parameter should be an array of permitted scalars
# by mapping it to an empty array:
@@ -374,6 +374,7 @@ module ActionController
StringIO,
IO,
ActionDispatch::Http::UploadedFile,
+ Rack::Test::UploadedFile,
]
def permitted_scalar?(value)
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index e7e8905d7e..06e936cdb0 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -1,4 +1,3 @@
-require 'digest/md5'
require 'active_support/core_ext/class/attribute_accessors'
require 'monitor'
@@ -170,7 +169,7 @@ module ActionDispatch # :nodoc:
alias_method :status_message, :message
def respond_to?(method)
- if method.to_sym == :to_path
+ if method.to_s == 'to_path'
stream.respond_to?(:to_path)
else
super
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 43f26d696d..97ac462411 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/hash/slice'
+
module ActionDispatch
module Http
module URL
@@ -32,8 +35,12 @@ module ActionDispatch
params.reject! { |_,v| v.to_param.nil? }
result = build_host_url(options)
- if options[:trailing_slash] && !path.ends_with?('/')
- result << path.sub(/(\?|\z)/) { "/" + $& }
+ if options[:trailing_slash]
+ if path.include?('?')
+ result << path.sub(/\?/, '/\&')
+ else
+ result << path.sub(/[^\/]\z|\A\z/, '\&/')
+ end
else
result << path
end
diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb
index 4a571ec546..d37aa1fbe5 100644
--- a/actionpack/lib/action_dispatch/journey/path/pattern.rb
+++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb
@@ -20,7 +20,7 @@ module ActionDispatch
@separators = strexp.separators.join
@anchored = strexp.anchor
else
- raise "wtf bro: #{strexp}"
+ raise ArgumentError, "Bad expression: #{strexp}"
end
@names = nil
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 0f02d230d4..36a0db6e61 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/key_generator'
require 'active_support/message_verifier'
module ActionDispatch
@@ -110,13 +111,17 @@ module ActionDispatch
# $& => example.local
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
+ def self.options_for_env(env) #:nodoc:
+ { signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '',
+ encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '',
+ encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
+ token_key: env[TOKEN_KEY] }
+ end
+
def self.build(request)
env = request.env
key_generator = env[GENERATOR_KEY]
- options = { signed_cookie_salt: env[SIGNED_COOKIE_SALT],
- encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT],
- encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT],
- token_key: env[TOKEN_KEY] }
+ options = options_for_env env
host = request.host
secure = request.ssl?
@@ -200,7 +205,7 @@ module ActionDispatch
end
# Removes the cookie on the client machine by setting the value to an empty string
- # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
+ # and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
def delete(key, options = {})
return unless @cookies.has_key? key.to_s
@@ -405,7 +410,7 @@ module ActionDispatch
@encryptor.decrypt_and_verify(encrypted_message)
end
rescue ActiveSupport::MessageVerifier::InvalidSignature,
- ActiveSupport::MessageVerifier::InvalidMessage
+ ActiveSupport::MessageEncryptor::InvalidMessage
nil
end
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 0898ad82dd..0fa1e9b859 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -13,10 +13,7 @@ module ActionDispatch
end
end
- DEFAULT_PARSERS = {
- Mime::XML => :xml_simple,
- Mime::JSON => :json
- }
+ DEFAULT_PARSERS = { Mime::JSON => :json }
def initialize(app, parsers = {})
@app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
@@ -36,19 +33,13 @@ module ActionDispatch
return false if request.content_length.zero?
- mime_type = content_type_from_legacy_post_data_format_header(env) ||
- request.content_mime_type
-
- strategy = @parsers[mime_type]
+ strategy = @parsers[request.content_mime_type]
return false unless strategy
case strategy
when Proc
strategy.call(request.raw_post)
- when :xml_simple, :xml_node
- data = request.deep_munge(Hash.from_xml(request.body.read) || {})
- data.with_indifferent_access
when :json
data = ActiveSupport::JSON.decode(request.body)
data = {:_json => data} unless data.is_a?(Hash)
@@ -56,23 +47,12 @@ module ActionDispatch
else
false
end
- rescue Exception => e # YAML, XML or Ruby code block errors
+ rescue Exception => e # JSON or Ruby code block errors
logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
raise ParseError.new(e.message, e)
end
- def content_type_from_legacy_post_data_format_header(env)
- if x_post_format = env['HTTP_X_POST_DATA_FORMAT']
- case x_post_format.to_s.downcase
- when 'yaml' then return Mime::YAML
- when 'xml' then return Mime::XML
- end
- end
-
- nil
- end
-
def logger(env)
env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
end
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 4e36c9bb49..93a2b52996 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -2,14 +2,14 @@ module ActionDispatch
# This middleware calculates the IP address of the remote client that is
# making the request. It does this by checking various headers that could
# contain the address, and then picking the last-set address that is not
- # on the list of trusted IPs. This follows the precendent set by e.g.
+ # on the list of trusted IPs. This follows the precedent set by e.g.
# {the Tomcat server}[https://issues.apache.org/bugzilla/show_bug.cgi?id=50453],
# with {reasoning explained at length}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection]
# by @gingerlime. A more detailed explanation of the algorithm is given
# at GetIp#calculate_ip.
#
# Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
- # requires. Some Rack servers simply drop preceeding headers, and only report
+ # requires. Some Rack servers simply drop preceding headers, and only report
# the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
# If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn)
# then you should test your Rack server to make sure your data is good.
@@ -83,7 +83,7 @@ module ActionDispatch
# This constant contains a regular expression that validates every known
# form of IP v4 and v6 address, with or without abbreviations, adapted
- # from {this gist}[https://gist.github.com/1289635].
+ # from {this gist}[https://gist.github.com/gazay/1289635].
VALID_IP = %r{
(^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
(^(
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index ab24118f3e..550f4dbd0d 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -1,6 +1,6 @@
<% unless @exception.blamed_files.blank? %>
<% if (hide = @exception.blamed_files.length > 8) %>
- <a href="#" onclick="toggleTrace()">Toggle blamed files</a>
+ <a href="#" onclick="return toggleTrace()">Toggle blamed files</a>
<% end %>
<pre id="blame_trace" <%='style="display:none"' if hide %>><code><%= @exception.describe_blame %></code></pre>
<% end %>
@@ -21,12 +21,12 @@
<p><b>Parameters</b>:</p> <pre><%= request_dump %></pre>
<div class="details">
- <div class="summary"><a href="#" onclick="toggleSessionDump()">Toggle session dump</a></div>
+ <div class="summary"><a href="#" onclick="return toggleSessionDump()">Toggle session dump</a></div>
<div id="session_dump" style="display:none"><pre><%= debug_hash @request.session %></pre></div>
</div>
<div class="details">
- <div class="summary"><a href="#" onclick="toggleEnvDump()">Toggle env dump</a></div>
+ <div class="summary"><a href="#" onclick="return toggleEnvDump()">Toggle env dump</a></div>
<div id="env_dump" style="display:none"><pre><%= debug_hash @request.env.slice(*@request.class::ENV_METHODS) %></pre></div>
</div>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
index 9878c2747e..891c87ac27 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
@@ -121,6 +121,7 @@
var toggle = function(id) {
var s = document.getElementById(id).style;
s.display = s.display == 'none' ? 'block' : 'none';
+ return false;
}
var show = function(id) {
document.getElementById(id).style.display = 'block';
@@ -129,13 +130,13 @@
document.getElementById(id).style.display = 'none';
}
var toggleTrace = function() {
- toggle('blame_trace');
+ return toggle('blame_trace');
}
var toggleSessionDump = function() {
- toggle('session_dump');
+ return toggle('session_dump');
}
var toggleEnvDump = function() {
- toggle('env_dump');
+ return toggle('env_dump');
}
</script>
</head>
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index bc6dd7145c..d251de33df 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -1,4 +1,5 @@
require 'delegate'
+require 'active_support/core_ext/string/strip'
module ActionDispatch
module Routing
@@ -90,6 +91,13 @@ module ActionDispatch
routes_to_display = filter_routes(filter)
routes = collect_routes(routes_to_display)
+
+ if routes.none?
+ formatter.no_routes
+ return formatter.result
+ end
+
+ formatter.header routes
formatter.section routes
@engines.each do |name, engine_routes|
@@ -155,16 +163,40 @@ module ActionDispatch
@buffer << draw_section(routes)
end
+ def header(routes)
+ @buffer << draw_header(routes)
+ end
+
+ def no_routes
+ @buffer << <<-MESSAGE.strip_heredoc
+ You don't have any routes defined!
+
+ Please add some routes in config/routes.rb.
+
+ For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
+ MESSAGE
+ end
+
private
def draw_section(routes)
- name_width = routes.map { |r| r[:name].length }.max
- verb_width = routes.map { |r| r[:verb].length }.max
- path_width = routes.map { |r| r[:path].length }.max
+ name_width, verb_width, path_width = widths(routes)
routes.map do |r|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
end
end
+
+ def draw_header(routes)
+ name_width, verb_width, path_width = widths(routes)
+
+ "#{"Prefix".rjust(name_width)} #{"Verb".ljust(verb_width)} #{"URI Pattern".ljust(path_width)} Controller#Action"
+ end
+
+ def widths(routes)
+ [routes.map { |r| r[:name].length }.max,
+ routes.map { |r| r[:verb].length }.max,
+ routes.map { |r| r[:path].length }.max]
+ end
end
class HtmlTableFormatter
@@ -181,6 +213,23 @@ module ActionDispatch
@buffer << @view.render(partial: "routes/route", collection: routes)
end
+ # the header is part of the HTML page, so we don't construct it here.
+ def header(routes)
+ end
+
+ def no_routes
+ @buffer << <<-MESSAGE.strip_heredoc
+ <p>You don't have any routes defined!</p>
+ <ul>
+ <li>Please add some routes in <tt>config/routes.rb</tt>.</li>
+ <li>
+ For more information about routes, please see the Rails guide
+ <a href="http://guides.rubyonrails.org/routing.html">Rails Routing from the Outside In</a>.
+ </li>
+ </ul>
+ MESSAGE
+ end
+
def result
@view.raw @view.render(layout: "routes/table") {
@view.raw @buffer.join("\n")
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 34f5f80d4d..dba9ccbfa5 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -50,7 +50,6 @@ module ActionDispatch
class Mapping #:nodoc:
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix, :format]
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- SHORTHAND_REGEX = %r{/[\w/]+$}
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
attr_reader :scope, :path, :options, :requirements, :conditions, :defaults
@@ -111,17 +110,7 @@ module ActionDispatch
@options[:controller] ||= /.+?/
end
- if using_match_shorthand?(path_without_format, @options)
- to_shorthand = @options[:to].blank?
- @options[:to] ||= path_without_format.gsub(/\(.*\)/, "")[1..-1].sub(%r{/([^/]*)$}, '#\1')
- end
-
- @options.merge!(default_controller_and_action(to_shorthand))
- end
-
- # match "account/overview"
- def using_match_shorthand?(path, options)
- path && (options[:to] || options[:action]).nil? && path =~ SHORTHAND_REGEX
+ @options.merge!(default_controller_and_action)
end
def normalize_format!
@@ -135,15 +124,7 @@ module ActionDispatch
def normalize_requirements!
constraints.each do |key, requirement|
next unless segment_keys.include?(key) || key == :controller
-
- if requirement.source =~ ANCHOR_CHARACTERS_REGEX
- raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
- end
-
- if requirement.multiline?
- raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
- end
-
+ verify_regexp_requirement(requirement) if requirement.is_a?(Regexp)
@requirements[key] = requirement
end
@@ -156,6 +137,16 @@ module ActionDispatch
end
end
+ def verify_regexp_requirement(requirement)
+ if requirement.source =~ ANCHOR_CHARACTERS_REGEX
+ raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
+ end
+
+ if requirement.multiline?
+ raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
+ end
+ end
+
def normalize_defaults!
@defaults.merge!(scope[:defaults]) if scope[:defaults]
@defaults.merge!(options[:defaults]) if options[:defaults]
@@ -214,7 +205,7 @@ module ActionDispatch
Constraints.new(endpoint, blocks, @set.request_class)
end
- def default_controller_and_action(to_shorthand=nil)
+ def default_controller_and_action
if to.respond_to?(:call)
{ }
else
@@ -227,7 +218,7 @@ module ActionDispatch
controller ||= default_controller
action ||= default_action
- unless controller.is_a?(Regexp) || to_shorthand
+ unless controller.is_a?(Regexp)
controller = [@scope[:module], controller].compact.join("/").presence
end
@@ -246,7 +237,7 @@ module ActionDispatch
raise ArgumentError, "missing :action"
end
- if controller.is_a?(String) && controller !~ /\A[a-z_\/]+\z/
+ if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/
message = "'#{controller}' is not a supported controller name. This can lead to potential routing problems."
message << " See http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
raise ArgumentError, message
@@ -340,7 +331,6 @@ module ActionDispatch
# because this means it will be matched first. As this is the most popular route
# of most Rails applications, this is beneficial.
def root(options = {})
- options = { :to => options } if options.is_a?(String)
match '/', { :as => :root, :via => :get }.merge!(options)
end
@@ -437,11 +427,15 @@ module ActionDispatch
# end
#
# [:constraints]
- # Constrains parameters with a hash of regular expressions or an
- # object that responds to <tt>matches?</tt>
+ # Constrains parameters with a hash of regular expressions
+ # or an object that responds to <tt>matches?</tt>. In addition, constraints
+ # other than path can also be specified with any object
+ # that responds to <tt>===</tt> (eg. String, Array, Range, etc.).
#
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }
#
+ # match 'json_only', constraints: { format: 'json' }
+ #
# class Blacklist
# def matches?(request) request.remote_ip == '1.2.3.4' end
# end
@@ -1383,6 +1377,11 @@ module ActionDispatch
paths = [path] + rest
end
+ path_without_format = path.to_s.sub(/\(\.:format\)$/, '')
+ if using_match_shorthand?(path_without_format, options)
+ options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1')
+ end
+
options[:anchor] = true unless options.key?(:anchor)
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
@@ -1393,6 +1392,10 @@ module ActionDispatch
self
end
+ def using_match_shorthand?(path, options)
+ path && (options[:to] || options[:action]).nil? && path =~ %r{/[\w/]+$}
+ end
+
def decomposed_match(path, options) # :nodoc:
if on = options.delete(:on)
send(on) { decomposed_match(path, options) }
@@ -1429,7 +1432,15 @@ module ActionDispatch
@set.add_route(app, conditions, requirements, defaults, as, anchor)
end
- def root(options={})
+ def root(path, options={})
+ if path.is_a?(String)
+ options[:to] = path
+ elsif path.is_a?(Hash) and options.empty?
+ options = path
+ else
+ raise ArgumentError, "must be called with a path and/or options"
+ end
+
if @scope[:scope_level] == :resources
with_scope_level(:root) do
scope(parent_resource.path) do
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index ff86f87d49..ca31b5e02e 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -31,6 +31,8 @@ module ActionDispatch
# If any of the path parameters has a invalid encoding then
# raise since it's likely to trigger errors further on.
params.each do |key, value|
+ next unless value.respond_to?(:valid_encoding?)
+
unless value.valid_encoding?
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
end
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 94cbc8e478..5c87a9cd7c 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -3,7 +3,7 @@ module ActionPack
MAJOR = 4
MINOR = 0
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/actionpack/lib/action_view/dependency_tracker.rb b/actionpack/lib/action_view/dependency_tracker.rb
new file mode 100644
index 0000000000..a2a555dfcb
--- /dev/null
+++ b/actionpack/lib/action_view/dependency_tracker.rb
@@ -0,0 +1,93 @@
+require 'thread_safe'
+
+module ActionView
+ class DependencyTracker
+ @trackers = ThreadSafe::Cache.new
+
+ def self.find_dependencies(name, template)
+ tracker = @trackers[template.handler]
+
+ if tracker.present?
+ tracker.call(name, template)
+ else
+ []
+ end
+ end
+
+ def self.register_tracker(extension, tracker)
+ handler = Template.handler_for_extension(extension)
+ @trackers[handler] = tracker
+ end
+
+ def self.remove_tracker(handler)
+ @trackers.delete(handler)
+ end
+
+ class ERBTracker
+ EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
+
+ # Matches:
+ # render partial: "comments/comment", collection: commentable.comments
+ # render "comments/comments"
+ # render 'comments/comments'
+ # render('comments/comments')
+ #
+ # render(@topic) => render("topics/topic")
+ # render(topics) => render("topics/topic")
+ # render(message.topics) => render("topics/topic")
+ RENDER_DEPENDENCY = /
+ render\s* # render, followed by optional whitespace
+ \(? # start an optional parenthesis for the render call
+ (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
+ ([@a-z"'][@a-z_\/\."']+) # the template name itself -- 2nd capture
+ /x
+
+ def self.call(name, template)
+ new(name, template).dependencies
+ end
+
+ def initialize(name, template)
+ @name, @template = name, template
+ end
+
+ def dependencies
+ render_dependencies + explicit_dependencies
+ end
+
+ attr_reader :name, :template
+ private :name, :template
+
+ private
+
+ def source
+ template.source
+ end
+
+ def directory
+ name.split("/")[0..-2].join("/")
+ end
+
+ def render_dependencies
+ source.scan(RENDER_DEPENDENCY).
+ collect(&:second).uniq.
+
+ # render(@topic) => render("topics/topic")
+ # render(topics) => render("topics/topic")
+ # render(message.topics) => render("topics/topic")
+ collect { |name| name.sub(/\A@?([a-z]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
+
+ # render("headline") => render("message/headline")
+ collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }.
+
+ # replace quotes from string renders
+ collect { |name| name.gsub(/["']/, "") }
+ end
+
+ def explicit_dependencies
+ source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
+ end
+ end
+
+ register_tracker :erb, ERBTracker
+ end
+end
diff --git a/actionpack/lib/action_view/digestor.rb b/actionpack/lib/action_view/digestor.rb
index 4507861dcc..9324a1ac50 100644
--- a/actionpack/lib/action_view/digestor.rb
+++ b/actionpack/lib/action_view/digestor.rb
@@ -1,25 +1,8 @@
require 'thread_safe'
+require 'action_view/dependency_tracker'
module ActionView
class Digestor
- EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
-
- # Matches:
- # render partial: "comments/comment", collection: commentable.comments
- # render "comments/comments"
- # render 'comments/comments'
- # render('comments/comments')
- #
- # render(@topic) => render("topics/topic")
- # render(topics) => render("topics/topic")
- # render(message.topics) => render("topics/topic")
- RENDER_DEPENDENCY = /
- render\s* # render, followed by optional whitespace
- \(? # start an optional parenthesis for the render call
- (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
- ([@a-z"'][@a-z_\/\."']+) # the template name itself -- 2nd capture
- /x
-
cattr_reader(:cache)
@@cache = ThreadSafe::Cache.new
@@ -47,7 +30,7 @@ module ActionView
end
def dependencies
- render_dependencies + explicit_dependencies
+ DependencyTracker.find_dependencies(name, template)
rescue ActionView::MissingTemplate
[] # File doesn't exist, so no dependencies
end
@@ -69,16 +52,16 @@ module ActionView
name.gsub(%r|/_|, "/")
end
- def directory
- name.split("/")[0..-2].join("/")
- end
-
def partial?
false
end
+ def template
+ @template ||= finder.find(logical_name, [], partial?, formats: [ format ])
+ end
+
def source
- @source ||= finder.find(logical_name, [], partial?, formats: [ format ]).source
+ template.source
end
def dependency_digest
@@ -89,26 +72,6 @@ module ActionView
(template_digests + injected_dependencies).join("-")
end
- def render_dependencies
- source.scan(RENDER_DEPENDENCY).
- collect(&:second).uniq.
-
- # render(@topic) => render("topics/topic")
- # render(topics) => render("topics/topic")
- # render(message.topics) => render("topics/topic")
- collect { |name| name.sub(/\A@?([a-z]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
-
- # render("headline") => render("message/headline")
- collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }.
-
- # replace quotes from string renders
- collect { |name| name.gsub(/["']/, "") }
- end
-
- def explicit_dependencies
- source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
- end
-
def injected_dependencies
Array.wrap(options[:dependencies])
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 5b3a2cae7c..31e37893c6 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -214,10 +214,24 @@ module ActionView
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.
+ # The +src+ argument is meant to be an image file path.
+ # The method removes the basename of the file path and the digest,
+ # if any. It also removes hyphens and underscores from file names and
+ # replaces them with spaces, returning a space-separated, titleized
+ # string.
+ #
+ # ==== Examples
+ #
+ # image_tag('rails.png')
+ # # => <img alt="Rails" src="/assets/rails.png" />
+ #
+ # image_tag('hyphenated-file-name.png')
+ # # => <img alt="Hyphenated file name" src="/assets/hyphenated-file-name.png" />
+ #
+ # image_tag('underscored_file_name.png')
+ # # => <img alt="Underscored file name" src="/assets/underscored_file_name.png" />
def image_alt(src)
- File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize
+ File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').tr('-_', ' ').capitalize
end
# Returns an html video tag for the +sources+. If +sources+ is a string,
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 61f939bff1..d3953c26b7 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -13,7 +13,7 @@ module ActionView
# elements. All of the select-type methods share a number of common options that are as follows:
#
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
- # would give \birthday[month] instead of \date[month] if passed to the <tt>select_month</tt> method.
+ # would give \birthday[month] instead of \date[month] if passed to the <tt>select_month</tt> method.
# * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
# * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
# the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
@@ -642,6 +642,8 @@ module ActionView
# <time datetime="2010-11-03">Yesterday</time>
# time_tag Date.today, pubdate: true # =>
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
+ # time_tag Date.today, datetime: Date.today.strftime('%G-W%V') # =>
+ # <time datetime="2010-W44">November 04, 2010</time>
#
# <%= time_tag Time.now do %>
# <span>Right now</span>
@@ -651,7 +653,7 @@ module ActionView
options = args.extract_options!
format = options.delete(:format) || :long
content = args.first || I18n.l(date_or_time, :format => format)
- datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
+ datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
end
diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb
index 34fc23ac1a..c29c1b1eea 100644
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -32,7 +32,7 @@ module ActionView
content_tag(:pre, object, :class => "debug_dump")
rescue Exception # errors from Marshal or YAML
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
- content_tag(:code, object.to_yaml, :class => "debug_dump")
+ content_tag(:code, object.inspect, :class => "debug_dump")
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 8b3a37a853..377819a80c 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -565,14 +565,14 @@ module ActionView
if priority_zones
if priority_zones.is_a?(Regexp)
- priority_zones = zones.select { |z| z =~ priority_zones }
+ priority_zones = zones.grep(priority_zones)
end
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
zone_options.safe_concat content_tag(:option, '-------------', :value => '', :disabled => 'disabled')
zone_options.safe_concat "\n"
- zones.reject! { |z| priority_zones.include?(z) }
+ zones = zones - priority_zones
end
zone_options.safe_concat options_for_select(convert_zones[zones], selected)
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 86d9f94067..1adc8225f1 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -33,7 +33,7 @@ module ActionView
# (by passing <tt>false</tt>). Remote forms may omit the embedded authenticity token
# by setting <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
# This is helpful when you're fragment-caching the form. Remote forms get the
- # authenticity from the <tt>meta</tt> tag, so embedding is unnecessary unless you
+ # authenticity token from the <tt>meta</tt> tag, so embedding is unnecessary unless you
# support browsers without JavaScript.
# * A list of parameters to feed to the URL the form will be posted to.
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 5e20b557d8..775d93ed39 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -241,13 +241,13 @@ module ActionView
# # </div>
# # </form>"
#
- # <%= button_to "New", action: "new", form_class: "new-thing" %>
+ # <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
# # => "<form method="post" action="/controller/new" class="new-thing">
# # <div><input value="New" type="submit" /></div>
# # </form>"
#
#
- # <%= button_to "Create", action: "create", remote: true, form: { "data-type" => "json" } %>
+ # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
# # <div>
# # <input value="Create" type="submit" />
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index ed013e2185..a9c62899b5 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -80,6 +80,13 @@ class RespondToController < ActionController::Base
respond_to(:html, :xml)
end
+ def using_defaults_with_all
+ respond_to do |type|
+ type.html
+ type.all{ render text: "ALL" }
+ end
+ end
+
def made_for_content_type
respond_to do |type|
type.rss { render :text => "RSS" }
@@ -301,6 +308,20 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal "<p>Hello world!</p>\n", @response.body
end
+ def test_using_defaults_with_all
+ @request.accept = "*/*"
+ get :using_defaults_with_all
+ assert_equal "HTML!", @response.body.strip
+
+ @request.accept = "text/html"
+ get :using_defaults_with_all
+ assert_equal "HTML!", @response.body.strip
+
+ @request.accept = "application/json"
+ get :using_defaults_with_all
+ assert_equal "ALL", @response.body
+ end
+
def test_using_defaults_with_type_list
@request.accept = "*/*"
get :using_defaults_with_type_list
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index aadb142660..437da43d9b 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -32,7 +32,8 @@ class ParametersPermitTest < ActiveSupport::TestCase
values += [0, 1.0, 2**128, BigDecimal.new(1)]
values += [true, false]
values += [Date.today, Time.now, DateTime.now]
- values += [STDOUT, StringIO.new, ActionDispatch::Http::UploadedFile.new(tempfile: __FILE__)]
+ values += [STDOUT, StringIO.new, ActionDispatch::Http::UploadedFile.new(tempfile: __FILE__),
+ Rack::Test::UploadedFile.new(__FILE__)]
values.each do |value|
params = ActionController::Parameters.new(id: value)
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 7571192f97..c272e785c2 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -66,6 +66,19 @@ class RequestForgeryProtectionControllerUsingException < ActionController::Base
protect_from_forgery :only => %w(index meta), :with => :exception
end
+class RequestForgeryProtectionControllerUsingNullSession < ActionController::Base
+ protect_from_forgery :with => :null_session
+
+ def signed
+ cookies.signed[:foo] = 'bar'
+ render :nothing => true
+ end
+
+ def encrypted
+ cookies.encrypted[:foo] = 'bar'
+ render :nothing => true
+ end
+end
class FreeCookieController < RequestForgeryProtectionControllerUsingResetSession
self.allow_forgery_protection = false
@@ -287,6 +300,28 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController
end
end
+class NullSessionDummyKeyGenerator
+ def generate_key(secret)
+ '03312270731a2ed0d11ed091c2338a06'
+ end
+end
+
+class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase
+ def setup
+ @request.env[ActionDispatch::Cookies::GENERATOR_KEY] = NullSessionDummyKeyGenerator.new
+ end
+
+ test 'should allow to set signed cookies' do
+ post :signed
+ assert_response :ok
+ end
+
+ test 'should allow to set encrypted cookies' do
+ post :encrypted
+ assert_response :ok
+ end
+end
+
class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::TestCase
include RequestForgeryProtectionTests
def assert_blocked
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 5e821046db..93e94f0f48 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -456,6 +456,22 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
assert_equal("/", routes.send(:root_path))
end
+ def test_named_route_root_with_hash
+ rs.draw do
+ root "hello#index", as: :index
+ end
+
+ routes = setup_for_named_route
+ assert_equal("http://test.host/", routes.send(:index_url))
+ assert_equal("/", routes.send(:index_path))
+ end
+
+ def test_root_without_path_raises_argument_error
+ assert_raises ArgumentError do
+ rs.draw { root nil }
+ end
+ end
+
def test_named_route_root_with_trailing_slash
rs.draw do
root "hello#index"
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index 19d5652d81..b2dfd96606 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -32,169 +32,53 @@ class WebServiceTest < ActionDispatch::IntegrationTest
end
end
- def test_post_xml
+ def test_post_json
with_test_route_set do
- post "/", '<entry attributed="true"><summary>content...</summary></entry>',
- {'CONTENT_TYPE' => 'application/xml'}
+ post "/", '{"entry":{"summary":"content..."}}', 'CONTENT_TYPE' => 'application/json'
assert_equal 'entry', @controller.response.body
assert @controller.params.has_key?(:entry)
assert_equal 'content...', @controller.params["entry"]['summary']
- assert_equal 'true', @controller.params["entry"]['attributed']
end
end
- def test_put_xml
+ def test_put_json
with_test_route_set do
- put "/", '<entry attributed="true"><summary>content...</summary></entry>',
- {'CONTENT_TYPE' => 'application/xml'}
+ put "/", '{"entry":{"summary":"content..."}}', 'CONTENT_TYPE' => 'application/json'
assert_equal 'entry', @controller.response.body
assert @controller.params.has_key?(:entry)
assert_equal 'content...', @controller.params["entry"]['summary']
- assert_equal 'true', @controller.params["entry"]['attributed']
end
end
- def test_put_xml_using_a_type_node
+ def test_register_and_use_json_simple
with_test_route_set do
- put "/", '<type attributed="true"><summary>content...</summary></type>',
- {'CONTENT_TYPE' => 'application/xml'}
-
- assert_equal 'type', @controller.response.body
- assert @controller.params.has_key?(:type)
- assert_equal 'content...', @controller.params["type"]['summary']
- assert_equal 'true', @controller.params["type"]['attributed']
- end
- end
-
- def test_put_xml_using_a_type_node_and_attribute
- with_test_route_set do
- put "/", '<type attributed="true"><summary type="boolean">false</summary></type>',
- {'CONTENT_TYPE' => 'application/xml'}
-
- assert_equal 'type', @controller.response.body
- assert @controller.params.has_key?(:type)
- assert_equal false, @controller.params["type"]['summary']
- assert_equal 'true', @controller.params["type"]['attributed']
- end
- end
-
- def test_post_xml_using_a_type_node
- with_test_route_set do
- post "/", '<font attributed="true"><type>arial</type></font>',
- {'CONTENT_TYPE' => 'application/xml'}
-
- assert_equal 'font', @controller.response.body
- assert @controller.params.has_key?(:font)
- assert_equal 'arial', @controller.params['font']['type']
- assert_equal 'true', @controller.params["font"]['attributed']
- end
- end
-
- def test_post_xml_using_a_root_node_named_type
- with_test_route_set do
- post "/", '<type type="integer">33</type>',
- {'CONTENT_TYPE' => 'application/xml'}
-
- assert @controller.params.has_key?(:type)
- assert_equal 33, @controller.params['type']
- end
- end
-
- def test_post_xml_using_an_attributted_node_named_type
- with_test_route_set do
- with_params_parsers Mime::XML => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do
- post "/", '<request><type type="string">Arial,12</type><z>3</z></request>',
- {'CONTENT_TYPE' => 'application/xml'}
-
- assert_equal 'type, z', @controller.response.body
- assert @controller.params.has_key?(:type)
- assert_equal 'Arial,12', @controller.params['type'], @controller.params.inspect
- assert_equal '3', @controller.params['z'], @controller.params.inspect
- end
- end
- end
-
- def test_post_xml_using_a_disallowed_type_attribute
- $stderr = StringIO.new
- with_test_route_set do
- post '/', '<foo type="symbol">value</foo>', 'CONTENT_TYPE' => 'application/xml'
- assert_response 500
-
- post '/', '<foo type="yaml">value</foo>', 'CONTENT_TYPE' => 'application/xml'
- assert_response 500
- end
- ensure
- $stderr = STDERR
- end
-
- def test_register_and_use_xml_simple
- with_test_route_set do
- with_params_parsers Mime::XML => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do
- post "/", '<request><summary>content...</summary><title>SimpleXml</title></request>',
- {'CONTENT_TYPE' => 'application/xml'}
+ with_params_parsers Mime::JSON => Proc.new { |data| JSON.parse(data)['request'].with_indifferent_access } do
+ post "/", '{"request":{"summary":"content...","title":"JSON"}}',
+ 'CONTENT_TYPE' => 'application/json'
assert_equal 'summary, title', @controller.response.body
assert @controller.params.has_key?(:summary)
assert @controller.params.has_key?(:title)
assert_equal 'content...', @controller.params["summary"]
- assert_equal 'SimpleXml', @controller.params["title"]
+ assert_equal 'JSON', @controller.params["title"]
end
end
end
- def test_use_xml_ximple_with_empty_request
+ def test_use_json_with_empty_request
with_test_route_set do
- assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} }
+ assert_nothing_raised { post "/", "", 'CONTENT_TYPE' => 'application/json' }
assert_equal '', @controller.response.body
end
end
- def test_dasherized_keys_as_xml
- with_test_route_set do
- post "/?full=1", "<first-key>\n<sub-key>...</sub-key>\n</first-key>",
- {'CONTENT_TYPE' => 'application/xml'}
- assert_equal 'action, controller, first_key(sub_key), full', @controller.response.body
- assert_equal "...", @controller.params[:first_key][:sub_key]
- end
- end
-
- def test_typecast_as_xml
- with_test_route_set do
- xml = <<-XML
- <data>
- <a type="integer">15</a>
- <b type="boolean">false</b>
- <c type="boolean">true</c>
- <d type="date">2005-03-17</d>
- <e type="datetime">2005-03-17T21:41:07Z</e>
- <f>unparsed</f>
- <g type="integer">1</g>
- <g>hello</g>
- <g type="date">1974-07-25</g>
- </data>
- XML
- post "/", xml, {'CONTENT_TYPE' => 'application/xml'}
-
- params = @controller.params
- assert_equal 15, params[:data][:a]
- assert_equal false, params[:data][:b]
- assert_equal true, params[:data][:c]
- assert_equal Date.new(2005,3,17), params[:data][:d]
- assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e]
- assert_equal "unparsed", params[:data][:f]
- assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g]
- end
- end
-
- def test_entities_unescaped_as_xml_simple
+ def test_dasherized_keys_as_json
with_test_route_set do
- xml = <<-XML
- <data>&lt;foo &quot;bar&apos;s&quot; &amp; friends&gt;</data>
- XML
- post "/", xml, {'CONTENT_TYPE' => 'application/xml'}
- assert_equal %(<foo "bar's" & friends>), @controller.params[:data]
+ post "/?full=1", '{"first-key":{"sub-key":"..."}}', 'CONTENT_TYPE' => 'application/json'
+ assert_equal 'action, controller, first-key(sub-key), full', @controller.response.body
+ assert_equal "...", @controller.params['first-key']['sub-key']
end
end
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 66d2cade22..5ada5a7603 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -72,6 +72,11 @@ class CookiesTest < ActionController::TestCase
head :ok
end
+ def set_invalid_encrypted_cookie
+ cookies[:invalid_cookie] = 'invalid--9170e00a57cfc27083363b5c75b835e477bd90cf'
+ head :ok
+ end
+
def raise_data_overflow
cookies.signed[:foo] = 'bye!' * 1024
head :ok
@@ -336,6 +341,11 @@ class CookiesTest < ActionController::TestCase
assert_equal 45, @controller.send(:cookies).signed[:user_id]
end
+ def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature
+ get :set_signed_cookie
+ assert_nil @controller.send(:cookies).signed[:non_existant_attribute]
+ end
+
def test_encrypted_cookie
get :set_encrypted_cookie
cookies = @controller.send :cookies
@@ -346,9 +356,14 @@ class CookiesTest < ActionController::TestCase
assert_equal 'bar', cookies.encrypted[:foo]
end
- def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature
- get :set_signed_cookie
- assert_nil @controller.send(:cookies).signed[:non_existant_attribute]
+ def test_accessing_nonexistant_encrypted_cookie_should_not_raise_invalid_message
+ get :set_encrypted_cookie
+ assert_nil @controller.send(:cookies).encrypted[:non_existant_attribute]
+ end
+
+ def test_setting_invalid_encrypted_cookie_should_return_nil_when_accessing_it
+ get :set_invalid_encrypted_cookie
+ assert_nil @controller.send(:cookies).encrypted[:invalid_cookie]
end
def test_permanent_signed_cookie
diff --git a/actionpack/test/dispatch/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb
deleted file mode 100644
index f13b64a3c7..0000000000
--- a/actionpack/test/dispatch/request/xml_params_parsing_test.rb
+++ /dev/null
@@ -1,182 +0,0 @@
-require 'abstract_unit'
-
-class XmlParamsParsingTest < ActionDispatch::IntegrationTest
- class TestController < ActionController::Base
- class << self
- attr_accessor :last_request_parameters
- end
-
- def parse
- self.class.last_request_parameters = request.request_parameters
- head :ok
- end
- end
-
- def teardown
- TestController.last_request_parameters = nil
- end
-
- test "parses a strict rack.input" do
- class Linted
- undef call if method_defined?(:call)
- def call(env)
- bar = env['action_dispatch.request.request_parameters']['foo']
- result = "<ok>#{bar}</ok>"
- [200, {"Content-Type" => "application/xml", "Content-Length" => result.length.to_s}, [result]]
- end
- end
- req = Rack::MockRequest.new(ActionDispatch::ParamsParser.new(Linted.new))
- resp = req.post('/', "CONTENT_TYPE" => "application/xml", :input => "<foo>bar</foo>", :lint => true)
- assert_equal "<ok>bar</ok>", resp.body
- end
-
- def assert_parses(expected, xml)
- with_test_routing do
- post "/parse", xml, default_headers
- assert_response :ok
- assert_equal(expected, TestController.last_request_parameters)
- end
- end
-
- test "nils are stripped from collections" do
- assert_parses(
- {"hash" => { "person" => nil} },
- "<hash><person type=\"array\"><person nil=\"true\"/></person></hash>")
- assert_parses(
- {"hash" => { "person" => ['foo']} },
- "<hash><person type=\"array\"><person>foo</person><person nil=\"true\"/></person>\n</hash>")
- end
-
- test "parses hash params" do
- with_test_routing do
- xml = "<person><name>David</name></person>"
- post "/parse", xml, default_headers
- assert_response :ok
- assert_equal({"person" => {"name" => "David"}}, TestController.last_request_parameters)
- end
- end
-
- test "parses single file" do
- with_test_routing do
- xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{::Base64.encode64('ABC')}</avatar></person>"
- post "/parse", xml, default_headers
- assert_response :ok
-
- person = TestController.last_request_parameters
- assert_equal "image/jpg", person['person']['avatar'].content_type
- assert_equal "me.jpg", person['person']['avatar'].original_filename
- assert_equal "ABC", person['person']['avatar'].read
- end
- end
-
- test "logs error if parsing unsuccessful" do
- with_test_routing do
- output = StringIO.new
- xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{::Base64.encode64('ABC')}</avatar></pineapple>"
- post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => ActiveSupport::Logger.new(output))
- assert_response :error
- output.rewind && err = output.read
- assert err =~ /Error occurred while parsing request parameters/
- end
- end
-
- test "occurring a parse error if parsing unsuccessful" do
- with_test_routing do
- begin
- $stderr = StringIO.new # suppress the log
- xml = "<person><name>David</name></pineapple>"
- exception = assert_raise(ActionDispatch::ParamsParser::ParseError) { post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => false) }
- assert_equal REXML::ParseException, exception.original_exception.class
- assert_equal exception.original_exception.message, exception.message
- ensure
- $stderr = STDERR
- end
- end
- end
-
- test "parses multiple files" do
- xml = <<-end_body
- <person>
- <name>David</name>
- <avatars>
- <avatar type='file' name='me.jpg' content_type='image/jpg'>#{::Base64.encode64('ABC')}</avatar>
- <avatar type='file' name='you.gif' content_type='image/gif'>#{::Base64.encode64('DEF')}</avatar>
- </avatars>
- </person>
- end_body
-
- with_test_routing do
- post "/parse", xml, default_headers
- assert_response :ok
- end
-
- person = TestController.last_request_parameters
-
- assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type
- assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename
- assert_equal "ABC", person['person']['avatars']['avatar'].first.read
-
- assert_equal "image/gif", person['person']['avatars']['avatar'].last.content_type
- assert_equal "you.gif", person['person']['avatars']['avatar'].last.original_filename
- assert_equal "DEF", person['person']['avatars']['avatar'].last.read
- end
-
- private
- def with_test_routing
- with_routing do |set|
- set.draw do
- post ':action', :to => ::XmlParamsParsingTest::TestController
- end
- yield
- end
- end
-
- def default_headers
- {'CONTENT_TYPE' => 'application/xml'}
- end
-end
-
-class LegacyXmlParamsParsingTest < XmlParamsParsingTest
- private
- def default_headers
- {'HTTP_X_POST_DATA_FORMAT' => 'xml'}
- end
-end
-
-class RootLessXmlParamsParsingTest < ActionDispatch::IntegrationTest
- class TestController < ActionController::Base
- wrap_parameters :person, :format => :xml
-
- class << self
- attr_accessor :last_request_parameters
- end
-
- def parse
- self.class.last_request_parameters = request.request_parameters
- head :ok
- end
- end
-
- def teardown
- TestController.last_request_parameters = nil
- end
-
- test "parses hash params" do
- with_test_routing do
- xml = "<name>David</name>"
- post "/parse", xml, {'CONTENT_TYPE' => 'application/xml'}
- assert_response :ok
- assert_equal({"name" => "David", "person" => {"name" => "David"}}, TestController.last_request_parameters)
- end
- end
-
- private
- def with_test_routing
- with_routing do |set|
- set.draw do
- post ':action', :to => ::RootLessXmlParamsParsingTest::TestController
- end
- yield
- end
- end
-end
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 8a01b29340..91810864d5 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -13,6 +13,9 @@ class RequestTest < ActiveSupport::TestCase
assert_equal '/books', url_for(:only_path => true, :path => '/books')
+ assert_equal 'http://www.example.com/books/?q=code', url_for(trailing_slash: true, path: '/books?q=code')
+ assert_equal 'http://www.example.com/books/?spareslashes=////', url_for(trailing_slash: true, path: '/books?spareslashes=////')
+
assert_equal 'http://www.example.com', url_for
assert_equal 'http://api.example.com', url_for(:subdomain => 'api')
assert_equal 'http://example.com', url_for(:subdomain => false)
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index 55221f87c4..234ae5764f 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -44,35 +44,45 @@ module ActionDispatch
mount engine => "/blog", :as => "blog"
end
- expected = [
+ assert_equal [
+ " Prefix Verb URI Pattern Controller#Action",
"custom_assets GET /custom/assets(.:format) custom_assets#show",
" blog /blog Blog::Engine",
"",
"Routes for Blog::Engine:",
"cart GET /cart(.:format) cart#show"
- ]
- assert_equal expected, output
+ ], output
end
def test_cart_inspect
output = draw do
get '/cart', :to => 'cart#show'
end
- assert_equal ["cart GET /cart(.:format) cart#show"], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ "cart GET /cart(.:format) cart#show"
+ ], output
end
def test_inspect_shows_custom_assets
output = draw do
get '/custom/assets', :to => 'custom_assets#show'
end
- assert_equal ["custom_assets GET /custom/assets(.:format) custom_assets#show"], output
+
+ assert_equal [
+ " Prefix Verb URI Pattern Controller#Action",
+ "custom_assets GET /custom/assets(.:format) custom_assets#show"
+ ], output
end
def test_inspect_routes_shows_resources_route
output = draw do
resources :articles
end
- expected = [
+
+ assert_equal [
+ " Prefix Verb URI Pattern Controller#Action",
" articles GET /articles(.:format) articles#index",
" POST /articles(.:format) articles#create",
" new_article GET /articles/new(.:format) articles#new",
@@ -80,50 +90,74 @@ module ActionDispatch
" article GET /articles/:id(.:format) articles#show",
" PATCH /articles/:id(.:format) articles#update",
" PUT /articles/:id(.:format) articles#update",
- " DELETE /articles/:id(.:format) articles#destroy" ]
- assert_equal expected, output
+ " DELETE /articles/:id(.:format) articles#destroy"
+ ], output
end
def test_inspect_routes_shows_root_route
output = draw do
root :to => 'pages#main'
end
- assert_equal ["root GET / pages#main"], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ "root GET / pages#main"
+ ], output
end
def test_inspect_routes_shows_dynamic_action_route
output = draw do
get 'api/:action' => 'api'
end
- assert_equal [" GET /api/:action(.:format) api#:action"], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " GET /api/:action(.:format) api#:action"
+ ], output
end
def test_inspect_routes_shows_controller_and_action_only_route
output = draw do
get ':controller/:action'
end
- assert_equal [" GET /:controller/:action(.:format) :controller#:action"], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " GET /:controller/:action(.:format) :controller#:action"
+ ], output
end
def test_inspect_routes_shows_controller_and_action_route_with_constraints
output = draw do
get ':controller(/:action(/:id))', :id => /\d+/
end
- assert_equal [" GET /:controller(/:action(/:id))(.:format) :controller#:action {:id=>/\\d+/}"], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " GET /:controller(/:action(/:id))(.:format) :controller#:action {:id=>/\\d+/}"
+ ], output
end
def test_rake_routes_shows_route_with_defaults
output = draw do
get 'photos/:id' => 'photos#show', :defaults => {:format => 'jpg'}
end
- assert_equal [%Q[ GET /photos/:id(.:format) photos#show {:format=>"jpg"}]], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ %Q[ GET /photos/:id(.:format) photos#show {:format=>"jpg"}]
+ ], output
end
def test_rake_routes_shows_route_with_constraints
output = draw do
get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
end
- assert_equal [" GET /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}"], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " GET /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}"
+ ], output
end
class RackApp
@@ -135,7 +169,11 @@ module ActionDispatch
output = draw do
get 'foo/:id' => RackApp, :id => /[A-Z]\d{5}/
end
- assert_equal [" GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}"], output
+
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}"
+ ], output
end
def test_rake_routes_shows_route_with_rack_app_nested_with_dynamic_constraints
@@ -151,7 +189,10 @@ module ActionDispatch
end
end
- assert_equal [" /foo #{RackApp.name} {:constraint=>( my custom constraint )}"], output
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " /foo #{RackApp.name} {:constraint=>( my custom constraint )}"
+ ], output
end
def test_rake_routes_dont_show_app_mounted_in_assets_prefix
@@ -169,9 +210,12 @@ module ActionDispatch
get "/foobar" => redirect{ "/foo/bar" }
end
- assert_equal " foo GET /foo(.:format) redirect(301, /foo/bar) {:subdomain=>\"admin\"}", output[0]
- assert_equal " bar GET /bar(.:format) redirect(307, path: /foo/bar)", output[1]
- assert_equal "foobar GET /foobar(.:format) redirect(301)", output[2]
+ assert_equal [
+ "Prefix Verb URI Pattern Controller#Action",
+ " foo GET /foo(.:format) redirect(301, /foo/bar) {:subdomain=>\"admin\"}",
+ " bar GET /bar(.:format) redirect(307, path: /foo/bar)",
+ "foobar GET /foobar(.:format) redirect(301)"
+ ], output
end
def test_routes_can_be_filtered
@@ -180,7 +224,8 @@ module ActionDispatch
resources :posts
end
- assert_equal [" posts GET /posts(.:format) posts#index",
+ assert_equal [" Prefix Verb URI Pattern Controller#Action",
+ " posts GET /posts(.:format) posts#index",
" POST /posts(.:format) posts#create",
" new_post GET /posts/new(.:format) posts#new",
"edit_post GET /posts/:id/edit(.:format) posts#edit",
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index fb1b8526d0..4775324b43 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1031,6 +1031,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'users/home#index', @response.body
end
+ def test_namespace_containing_numbers
+ draw do
+ namespace :v2 do
+ resources :subscriptions
+ end
+ end
+
+ get '/v2/subscriptions'
+ assert_equal 'v2/subscriptions#index', @response.body
+ assert_equal '/v2/subscriptions', v2_subscriptions_path
+ end
+
def test_articles_with_id
draw do
controller :articles do
@@ -1134,6 +1146,33 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'api/products#list', @response.body
end
+ def test_match_shorthand_inside_scope_with_variables_with_controller
+ draw do
+ scope ':locale' do
+ match 'questions/new', via: [:get]
+ end
+ end
+
+ get '/de/questions/new'
+ assert_equal 'questions#new', @response.body
+ assert_equal 'de', @request.params[:locale]
+ end
+
+ def test_match_shorthand_inside_nested_namespaces_and_scopes_with_controller
+ draw do
+ namespace :api do
+ namespace :v3 do
+ scope ':locale' do
+ get "products/list"
+ end
+ end
+ end
+ end
+
+ get '/api/v3/en/products/list'
+ assert_equal 'api/v3/products#list', @response.body
+ end
+
def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper
draw do
resources :replies do
@@ -1307,7 +1346,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'en', @request.params[:locale]
end
- def test_default_params
+ def test_default_string_params
draw do
get 'inline_pages/(:id)', :to => 'pages#show', :id => 'home'
get 'default_pages/(:id)', :to => 'pages#show', :defaults => { :id => 'home' }
@@ -1327,6 +1366,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'home', @request.params[:id]
end
+ def test_default_integer_params
+ draw do
+ get 'inline_pages/(:page)', to: 'pages#show', page: 1
+ get 'default_pages/(:page)', to: 'pages#show', defaults: { page: 1 }
+
+ defaults page: 1 do
+ get 'scoped_pages/(:page)', to: 'pages#show'
+ end
+ end
+
+ get '/inline_pages'
+ assert_equal 1, @request.params[:page]
+
+ get '/default_pages'
+ assert_equal 1, @request.params[:page]
+
+ get '/scoped_pages'
+ assert_equal 1, @request.params[:page]
+ end
+
def test_resource_constraints
draw do
resources :products, :constraints => { :id => /\d{4}/ } do
@@ -3341,6 +3400,42 @@ class TestPortConstraints < ActionDispatch::IntegrationTest
end
end
+class TestFormatConstraints < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ get '/string', to: ok, constraints: { format: 'json' }
+ get '/regexp', to: ok, constraints: { format: /json/ }
+ end
+ end
+
+ include Routes.url_helpers
+ def app; Routes end
+
+ def test_string_format_constraints
+ get 'http://www.example.com/string'
+ assert_response :success
+
+ get 'http://www.example.com/string.json'
+ assert_response :success
+
+ get 'http://www.example.com/string.html'
+ assert_response :not_found
+ end
+
+ def test_regexp_format_constraints
+ get 'http://www.example.com/regexp'
+ assert_response :success
+
+ get 'http://www.example.com/regexp.json'
+ assert_response :success
+
+ get 'http://www.example.com/regexp.html'
+ assert_response :not_found
+ end
+end
+
class TestRouteDefaults < ActionDispatch::IntegrationTest
stub_controllers do |routes|
Routes = routes
diff --git a/actionpack/test/fixtures/respond_to/using_defaults_with_all.html.erb b/actionpack/test/fixtures/respond_to/using_defaults_with_all.html.erb
new file mode 100644
index 0000000000..9f1f855269
--- /dev/null
+++ b/actionpack/test/fixtures/respond_to/using_defaults_with_all.html.erb
@@ -0,0 +1 @@
+HTML!
diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb
index 2b7227cd0d..ce02104181 100644
--- a/actionpack/test/journey/path/pattern_test.rb
+++ b/actionpack/test/journey/path/pattern_test.rb
@@ -75,6 +75,10 @@ module ActionDispatch
end
end
+ def test_to_raise_exception_with_bad_expression
+ assert_raise(ArgumentError, "Bad expression: []") { Pattern.new [] }
+ end
+
def test_to_regexp_with_extended_group
strexp = Router::Strexp.new(
'/page/:name',
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 82c9d383ac..11614a45dc 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -443,7 +443,8 @@ class AssetTagHelperTest < ActionView::TestCase
[nil, '/', '/foo/bar/', 'foo/bar/'].each do |prefix|
assert_equal 'Rails', image_alt("#{prefix}rails.png")
assert_equal 'Rails', image_alt("#{prefix}rails-9c0a079bdd7701d7e729bd956823d153.png")
- assert_equal 'Avatar-0000', image_alt("#{prefix}avatar-0000.png")
+ assert_equal 'Long file name with hyphens', image_alt("#{prefix}long-file-name-with-hyphens.png")
+ assert_equal 'Long file name with underscores', image_alt("#{prefix}long_file_name_with_underscores.png")
end
end
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index f11adefad8..242b56a1fd 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -1510,42 +1510,42 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, date_select("post", "written_on")
end
-
+
def test_date_select_with_selected
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
-
+
expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
-
+
expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7" selected="selected">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
expected << "</select>\n"
-
+
expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10" selected="selected">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
-
+
expected << "</select>\n"
-
+
assert_dom_equal expected, date_select("post", "written_on", :selected => Date.new(2004, 07, 10))
end
def test_date_select_with_selected_nil
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
-
+
expected = '<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="1"/>' + "\n"
-
+
expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
expected << %{<option value=""></option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
expected << "</select>\n"
-
+
expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
expected << %{<option value=""></option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
-
+
expected << "</select>\n"
-
+
assert_dom_equal expected, date_select("post", "written_on", include_blank: true, discard_year: true, selected: nil)
end
@@ -2296,7 +2296,7 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", discard_year: true, selected: nil)
end
-
+
def test_datetime_select_defaults_to_time_zone_now_when_config_time_zone_is_set
# The love zone is UTC+0
mytz = Class.new(ActiveSupport::TimeZone) {
@@ -3160,14 +3160,14 @@ class DateHelperTest < ActionView::TestCase
end
def test_time_tag_with_date
- date = Date.today
- expected = "<time datetime=\"#{date.rfc3339}\">#{I18n.l(date, :format => :long)}</time>"
+ date = Date.new(2013, 2, 20)
+ expected = '<time datetime="2013-02-20">February 20, 2013</time>'
assert_equal expected, time_tag(date)
end
def test_time_tag_with_time
- time = Time.now
- expected = "<time datetime=\"#{time.xmlschema}\">#{I18n.l(time, :format => :long)}</time>"
+ time = Time.new(2013, 2, 20, 0, 0, 0, '+00:00')
+ expected = '<time datetime="2013-02-20T00:00:00+00:00">February 20, 2013 00:00</time>'
assert_equal expected, time_tag(time)
end
@@ -3184,8 +3184,8 @@ class DateHelperTest < ActionView::TestCase
end
def test_time_tag_with_different_format
- time = Time.now
- expected = "<time datetime=\"#{time.xmlschema}\">#{I18n.l(time, :format => :short)}</time>"
+ time = Time.new(2013, 2, 20, 0, 0, 0, '+00:00')
+ expected = '<time datetime="2013-02-20T00:00:00+00:00">20 Feb 00:00</time>'
assert_equal expected, time_tag(time, :format => :short)
end
diff --git a/actionpack/test/template/dependency_tracker_test.rb b/actionpack/test/template/dependency_tracker_test.rb
new file mode 100644
index 0000000000..9c68afbdbd
--- /dev/null
+++ b/actionpack/test/template/dependency_tracker_test.rb
@@ -0,0 +1,46 @@
+require 'abstract_unit'
+require 'action_view/dependency_tracker'
+
+class DependencyTrackerTest < ActionView::TestCase
+ Neckbeard = lambda {|template| template.source }
+ Bowtie = lambda {|template| template.source }
+
+ class NeckbeardTracker
+ def self.call(name, template)
+ ["foo/#{name}"]
+ end
+ end
+
+ class FakeTemplate
+ attr_reader :source, :handler
+
+ def initialize(source, handler = Neckbeard)
+ @source, @handler = source, handler
+ end
+ end
+
+ def tracker
+ ActionView::DependencyTracker
+ end
+
+ def setup
+ ActionView::Template.register_template_handler :neckbeard, Neckbeard
+ tracker.register_tracker(:neckbeard, NeckbeardTracker)
+ end
+
+ def teardown
+ tracker.remove_tracker(:neckbeard)
+ end
+
+ def test_finds_tracker_by_template_handler
+ template = FakeTemplate.new("boo/hoo")
+ dependencies = tracker.find_dependencies("boo/hoo", template)
+ assert_equal ["foo/boo/hoo"], dependencies
+ end
+
+ def test_returns_empty_array_if_no_tracker_is_found
+ template = FakeTemplate.new("boo/hoo", Bowtie)
+ dependencies = tracker.find_dependencies("boo/hoo", template)
+ assert_equal [], dependencies
+ end
+end
diff --git a/actionpack/test/template/digestor_test.rb b/actionpack/test/template/digestor_test.rb
index 849e2981a6..e29cecabc0 100644
--- a/actionpack/test/template/digestor_test.rb
+++ b/actionpack/test/template/digestor_test.rb
@@ -2,10 +2,11 @@ require 'abstract_unit'
require 'fileutils'
class FixtureTemplate
- attr_reader :source
+ attr_reader :source, :handler
def initialize(template_path)
@source = File.read(template_path)
+ @handler = ActionView::Template.handler_for_extension(:erb)
rescue Errno::ENOENT
raise ActionView::MissingTemplate.new([], "", [], true, [])
end
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index c5efed3195..04cdd068c8 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -21,10 +21,10 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def setup
- @fake_timezones = %w(A B C D E).inject([]) do |zones, id|
+ @fake_timezones = %w(A B C D E).map do |id|
tz = TZInfo::Timezone.loaded_zones[id] = stub(:name => id, :to_s => id)
ActiveSupport::TimeZone.stubs(:[]).with(id).returns(tz)
- zones << tz
+ tz
end
ActiveSupport::TimeZone.stubs(:all).returns(@fake_timezones)
end
@@ -351,7 +351,7 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
- def test_time_zone_options_no_parms
+ def test_time_zone_options_no_params
opts = time_zone_options_for_select
assert_dom_equal "<option value=\"A\">A</option>\n" +
"<option value=\"B\">B</option>\n" +
@@ -417,6 +417,13 @@ class FormOptionsHelperTest < ActionView::TestCase
opts
end
+ def test_time_zone_options_with_priority_zones_does_not_mutate_time_zones
+ original_zones = ActiveSupport::TimeZone.all.dup
+ zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
+ time_zone_options_for_select(nil, zones)
+ assert_equal original_zones, ActiveSupport::TimeZone.all
+ end
+
def test_time_zone_options_returns_html_safe_string
assert time_zone_options_for_select.html_safe?
end
@@ -1079,11 +1086,13 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select_with_priority_zones_as_regexp
@firm = Firm.new("D")
+
+ priority_zones = /A|D/
@fake_timezones.each_with_index do |tz, i|
- tz.stubs(:=~).returns(i.zero? || i == 3)
+ priority_zones.stubs(:===).with(tz).returns(i.zero? || i == 3)
end
- html = time_zone_select("firm", "time_zone", /A|D/)
+ html = time_zone_select("firm", "time_zone", priority_zones)
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
"<option value=\"A\">A</option>\n" +
"<option value=\"D\" selected=\"selected\">D</option>" +
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index d8fffe75ed..6e640889d2 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -1,323 +1,75 @@
-require 'abstract_unit'
+require "abstract_unit"
class NumberHelperTest < ActionView::TestCase
tests ActionView::Helpers::NumberHelper
- def kilobytes(number)
- number * 1024
- end
-
- def megabytes(number)
- kilobytes(number) * 1024
- end
-
- def gigabytes(number)
- megabytes(number) * 1024
- end
-
- def terabytes(number)
- gigabytes(number) * 1024
- end
-
def test_number_to_phone
- assert_equal("555-1234", number_to_phone(5551234))
- assert_equal("800-555-1212", number_to_phone(8005551212))
- assert_equal("(800) 555-1212", number_to_phone(8005551212, {:area_code => true}))
- assert_equal("", number_to_phone("", {:area_code => true}))
- assert_equal("800 555 1212", number_to_phone(8005551212, {:delimiter => " "}))
- assert_equal("(800) 555-1212 x 123", number_to_phone(8005551212, {:area_code => true, :extension => 123}))
- assert_equal("800-555-1212", number_to_phone(8005551212, :extension => " "))
- assert_equal("555.1212", number_to_phone(5551212, :delimiter => '.'))
- assert_equal("800-555-1212", number_to_phone("8005551212"))
- assert_equal("+1-800-555-1212", number_to_phone(8005551212, :country_code => 1))
- assert_equal("+18005551212", number_to_phone(8005551212, :country_code => 1, :delimiter => ''))
- assert_equal("22-555-1212", number_to_phone(225551212))
- assert_equal("+45-22-555-1212", number_to_phone(225551212, :country_code => 45))
- assert_equal '111&lt;script&gt;&lt;/script&gt;111&lt;script&gt;&lt;/script&gt;1111', number_to_phone(1111111111, :delimiter => "<script></script>")
+ assert_equal nil, number_to_phone(nil)
+ assert_equal "555-1234", number_to_phone(5551234)
+ assert_equal "(800) 555-1212 x 123", number_to_phone(8005551212, area_code: true, extension: 123)
+ assert_equal "+18005551212", number_to_phone(8005551212, country_code: 1, delimiter: "")
end
def test_number_to_currency
- assert_equal("$1,234,567,890.50", number_to_currency(1234567890.50))
- assert_equal("$1,234,567,890.51", number_to_currency(1234567890.506))
- assert_equal("-$1,234,567,890.50", number_to_currency(-1234567890.50))
- assert_equal("-$ 1,234,567,890.50", number_to_currency(-1234567890.50, {:format => "%u %n"}))
- assert_equal("($1,234,567,890.50)", number_to_currency(-1234567890.50, {:negative_format => "(%u%n)"}))
- assert_equal("$1,234,567,892", number_to_currency(1234567891.50, {:precision => 0}))
- assert_equal("$1,234,567,890.5", number_to_currency(1234567890.50, {:precision => 1}))
- assert_equal("&pound;1234567890,50", number_to_currency(1234567890.50, {:unit => "&pound;", :separator => ",", :delimiter => ""}))
- assert_equal("$1,234,567,890.50", number_to_currency("1234567890.50"))
- assert_equal("1,234,567,890.50 K&#269;", number_to_currency("1234567890.50", {:unit => "K&#269;", :format => "%n %u"}))
- assert_equal("1,234,567,890.50 - K&#269;", number_to_currency("-1234567890.50", {:unit => "K&#269;", :format => "%n %u", :negative_format => "%n - %u"}))
- assert_equal '$1&lt;script&gt;&lt;/script&gt;01', number_to_currency(1.01, :separator => "<script></script>")
- assert_equal '$1&lt;script&gt;&lt;/script&gt;000.00', number_to_currency(1000, :delimiter => "<script></script>")
+ assert_equal nil, number_to_currency(nil)
+ assert_equal "$1,234,567,890.50", number_to_currency(1234567890.50)
+ assert_equal "$1,234,567,892", number_to_currency(1234567891.50, precision: 0)
+ assert_equal "1,234,567,890.50 - K&#269;", number_to_currency("-1234567890.50", unit: "K&#269;", format: "%n %u", negative_format: "%n - %u")
end
def test_number_to_percentage
- assert_equal("100.000%", number_to_percentage(100))
- assert_equal("100%", number_to_percentage(100, {:precision => 0}))
- assert_equal("302.06%", number_to_percentage(302.0574, {:precision => 2}))
- assert_equal("100.000%", number_to_percentage("100"))
- assert_equal("1000.000%", number_to_percentage("1000"))
- assert_equal("123.4%", number_to_percentage(123.400, :precision => 3, :strip_insignificant_zeros => true))
- assert_equal("1.000,000%", number_to_percentage(1000, :delimiter => '.', :separator => ','))
- assert_equal("1000.000 %", number_to_percentage(1000, :format => "%n %"))
- assert_equal '1&lt;script&gt;&lt;/script&gt;010%', number_to_percentage(1.01, :separator => "<script></script>")
- assert_equal '1&lt;script&gt;&lt;/script&gt;000.000%', number_to_percentage(1000, :delimiter => "<script></script>")
+ assert_equal nil, number_to_percentage(nil)
+ assert_equal "100.000%", number_to_percentage(100)
+ assert_equal "100%", number_to_percentage(100, precision: 0)
+ assert_equal "123.4%", number_to_percentage(123.400, precision: 3, strip_insignificant_zeros: true)
+ assert_equal "1.000,000%", number_to_percentage(1000, delimiter: ".", separator: ",")
end
def test_number_with_delimiter
- assert_equal("12,345,678", number_with_delimiter(12345678))
- assert_equal("0", number_with_delimiter(0))
- assert_equal("123", number_with_delimiter(123))
- assert_equal("123,456", number_with_delimiter(123456))
- assert_equal("123,456.78", number_with_delimiter(123456.78))
- assert_equal("123,456.789", number_with_delimiter(123456.789))
- assert_equal("123,456.78901", number_with_delimiter(123456.78901))
- assert_equal("123,456,789.78901", number_with_delimiter(123456789.78901))
- assert_equal("0.78901", number_with_delimiter(0.78901))
- assert_equal("123,456.78", number_with_delimiter("123456.78"))
- end
-
- def test_number_with_delimiter_with_options_hash
- assert_equal '12 345 678', number_with_delimiter(12345678, :delimiter => ' ')
- assert_equal '12,345,678-05', number_with_delimiter(12345678.05, :separator => '-')
- assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :separator => ',', :delimiter => '.')
- assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :delimiter => '.', :separator => ',')
- assert_equal '1&lt;script&gt;&lt;/script&gt;01', number_with_delimiter(1.01, :separator => "<script></script>")
- assert_equal '1&lt;script&gt;&lt;/script&gt;000', number_with_delimiter(1000, :delimiter => "<script></script>")
+ assert_equal nil, number_with_delimiter(nil)
+ assert_equal "12,345,678", number_with_delimiter(12345678)
+ assert_equal "0", number_with_delimiter(0)
end
def test_number_with_precision
- assert_equal("-111.235", number_with_precision(-111.2346))
- assert_equal("111.235", number_with_precision(111.2346))
- assert_equal("31.83", number_with_precision(31.825, :precision => 2))
- assert_equal("111.23", number_with_precision(111.2346, :precision => 2))
- assert_equal("111.00", number_with_precision(111, :precision => 2))
- assert_equal("111.235", number_with_precision("111.2346"))
- assert_equal("31.83", number_with_precision("31.825", :precision => 2))
- assert_equal("3268", number_with_precision((32.6751 * 100.00), :precision => 0))
- assert_equal("112", number_with_precision(111.50, :precision => 0))
- assert_equal("1234567892", number_with_precision(1234567891.50, :precision => 0))
- assert_equal("0", number_with_precision(0, :precision => 0))
- assert_equal("0.00100", number_with_precision(0.001, :precision => 5))
- assert_equal("0.001", number_with_precision(0.00111, :precision => 3))
- assert_equal("10.00", number_with_precision(9.995, :precision => 2))
- assert_equal("11.00", number_with_precision(10.995, :precision => 2))
- assert_equal("0.00", number_with_precision(-0.001, :precision => 2))
- end
-
- def test_number_with_precision_with_custom_delimiter_and_separator
- assert_equal '31,83', number_with_precision(31.825, :precision => 2, :separator => ',')
- assert_equal '1.231,83', number_with_precision(1231.825, :precision => 2, :separator => ',', :delimiter => '.')
- assert_equal '1&lt;script&gt;&lt;/script&gt;010', number_with_precision(1.01, :separator => "<script></script>")
- assert_equal '1&lt;script&gt;&lt;/script&gt;000.000', number_with_precision(1000, :delimiter => "<script></script>")
- end
-
- def test_number_with_precision_with_significant_digits
- assert_equal "124000", number_with_precision(123987, :precision => 3, :significant => true)
- assert_equal "120000000", number_with_precision(123987876, :precision => 2, :significant => true )
- assert_equal "40000", number_with_precision("43523", :precision => 1, :significant => true )
- assert_equal "9775", number_with_precision(9775, :precision => 4, :significant => true )
- assert_equal "5.4", number_with_precision(5.3923, :precision => 2, :significant => true )
- assert_equal "5", number_with_precision(5.3923, :precision => 1, :significant => true )
- assert_equal "1", number_with_precision(1.232, :precision => 1, :significant => true )
- assert_equal "7", number_with_precision(7, :precision => 1, :significant => true )
- assert_equal "1", number_with_precision(1, :precision => 1, :significant => true )
- assert_equal "53", number_with_precision(52.7923, :precision => 2, :significant => true )
- assert_equal "9775.00", number_with_precision(9775, :precision => 6, :significant => true )
- assert_equal "5.392900", number_with_precision(5.3929, :precision => 7, :significant => true )
- assert_equal "0.0", number_with_precision(0, :precision => 2, :significant => true )
- assert_equal "0", number_with_precision(0, :precision => 1, :significant => true )
- assert_equal "0.0001", number_with_precision(0.0001, :precision => 1, :significant => true )
- assert_equal "0.000100", number_with_precision(0.0001, :precision => 3, :significant => true )
- assert_equal "0.0001", number_with_precision(0.0001111, :precision => 1, :significant => true )
- assert_equal "10.0", number_with_precision(9.995, :precision => 3, :significant => true)
- assert_equal "9.99", number_with_precision(9.994, :precision => 3, :significant => true)
- assert_equal "11.0", number_with_precision(10.995, :precision => 3, :significant => true)
- end
-
- def test_number_with_precision_with_strip_insignificant_zeros
- assert_equal "9775.43", number_with_precision(9775.43, :precision => 4, :strip_insignificant_zeros => true )
- assert_equal "9775.2", number_with_precision(9775.2, :precision => 6, :significant => true, :strip_insignificant_zeros => true )
- assert_equal "0", number_with_precision(0, :precision => 6, :significant => true, :strip_insignificant_zeros => true )
- end
-
- def test_number_with_precision_with_significant_true_and_zero_precision
- # Zero precision with significant is a mistake (would always return zero),
- # so we treat it as if significant was false (increases backwards compatibility for number_to_human_size)
- assert_equal "124", number_with_precision(123.987, :precision => 0, :significant => true)
- assert_equal "12", number_with_precision(12, :precision => 0, :significant => true )
- assert_equal "12", number_with_precision("12.3", :precision => 0, :significant => true )
+ assert_equal nil, number_with_precision(nil)
+ assert_equal "-111.235", number_with_precision(-111.2346)
+ assert_equal "111.00", number_with_precision(111, precision: 2)
+ assert_equal "0.00100", number_with_precision(0.001, precision: 5)
end
def test_number_to_human_size
- assert_equal '0 Bytes', number_to_human_size(0)
- assert_equal '1 Byte', number_to_human_size(1)
- assert_equal '3 Bytes', number_to_human_size(3.14159265)
- assert_equal '123 Bytes', number_to_human_size(123.0)
- assert_equal '123 Bytes', number_to_human_size(123)
- assert_equal '1.21 KB', number_to_human_size(1234)
- assert_equal '12.1 KB', number_to_human_size(12345)
- assert_equal '1.18 MB', number_to_human_size(1234567)
- assert_equal '1.15 GB', number_to_human_size(1234567890)
- assert_equal '1.12 TB', number_to_human_size(1234567890123)
- assert_equal '1030 TB', number_to_human_size(terabytes(1026))
- assert_equal '444 KB', number_to_human_size(kilobytes(444))
- assert_equal '1020 MB', number_to_human_size(megabytes(1023))
- assert_equal '3 TB', number_to_human_size(terabytes(3))
- assert_equal '1.2 MB', number_to_human_size(1234567, :precision => 2)
- assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4)
- assert_equal '123 Bytes', number_to_human_size('123')
- assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 2)
- assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4)
- assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4)
- assert_equal '1 Byte', number_to_human_size(1.1)
- assert_equal '10 Bytes', number_to_human_size(10)
- end
-
- def test_number_to_human_size_with_si_prefix
- assert_equal '3 Bytes', number_to_human_size(3.14159265, :prefix => :si)
- assert_equal '123 Bytes', number_to_human_size(123.0, :prefix => :si)
- assert_equal '123 Bytes', number_to_human_size(123, :prefix => :si)
- assert_equal '1.23 KB', number_to_human_size(1234, :prefix => :si)
- assert_equal '12.3 KB', number_to_human_size(12345, :prefix => :si)
- assert_equal '1.23 MB', number_to_human_size(1234567, :prefix => :si)
- assert_equal '1.23 GB', number_to_human_size(1234567890, :prefix => :si)
- assert_equal '1.23 TB', number_to_human_size(1234567890123, :prefix => :si)
- end
-
- def test_number_to_human_size_with_options_hash
- assert_equal '1.2 MB', number_to_human_size(1234567, :precision => 2)
- assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4)
- assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 2)
- assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4)
- assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4)
- assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 1)
- assert_equal '500 MB', number_to_human_size(524288000, :precision=>3)
- assert_equal '10 MB', number_to_human_size(9961472, :precision=>0)
- assert_equal '40 KB', number_to_human_size(41010, :precision => 1)
- assert_equal '40 KB', number_to_human_size(41100, :precision => 2)
- assert_equal '1.0 KB', number_to_human_size(kilobytes(1.0123), :precision => 2, :strip_insignificant_zeros => false)
- assert_equal '1.012 KB', number_to_human_size(kilobytes(1.0123), :precision => 3, :significant => false)
- assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 0, :significant => true) #ignores significant it precision is 0
- assert_equal '9&lt;script&gt;&lt;/script&gt;86 KB', number_to_human_size(10100, :separator => "<script></script>")
- end
-
- def test_number_to_human_size_with_custom_delimiter_and_separator
- assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0123), :precision => 3, :separator => ',')
- assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4, :separator => ',')
- assert_equal '1.000,1 TB', number_to_human_size(terabytes(1000.1), :precision => 5, :delimiter => '.', :separator => ',')
+ assert_equal nil, number_to_human_size(nil)
+ assert_equal "3 Bytes", number_to_human_size(3.14159265)
+ assert_equal "1.2 MB", number_to_human_size(1234567, precision: 2)
end
def test_number_to_human
- assert_equal '-123', number_to_human(-123)
- assert_equal '-0.5', number_to_human(-0.5)
- assert_equal '0', number_to_human(0)
- assert_equal '0.5', number_to_human(0.5)
- assert_equal '123', number_to_human(123)
- assert_equal '1.23 Thousand', number_to_human(1234)
- assert_equal '12.3 Thousand', number_to_human(12345)
- assert_equal '1.23 Million', number_to_human(1234567)
- assert_equal '1.23 Billion', number_to_human(1234567890)
- assert_equal '1.23 Trillion', number_to_human(1234567890123)
- assert_equal '1.23 Quadrillion', number_to_human(1234567890123456)
- assert_equal '1230 Quadrillion', number_to_human(1234567890123456789)
- assert_equal '490 Thousand', number_to_human(489939, :precision => 2)
- assert_equal '489.9 Thousand', number_to_human(489939, :precision => 4)
- assert_equal '489 Thousand', number_to_human(489000, :precision => 4)
- assert_equal '489.0 Thousand', number_to_human(489000, :precision => 4, :strip_insignificant_zeros => false)
- assert_equal '1.2346 Million', number_to_human(1234567, :precision => 4, :significant => false)
- assert_equal '1,2 Million', number_to_human(1234567, :precision => 1, :significant => false, :separator => ',')
- assert_equal '1 Million', number_to_human(1234567, :precision => 0, :significant => true, :separator => ',') #significant forced to false
- end
-
- def test_number_to_human_with_custom_units
- #Only integers
- volume = {:unit => "ml", :thousand => "lt", :million => "m3"}
- assert_equal '123 lt', number_to_human(123456, :units => volume)
- assert_equal '12 ml', number_to_human(12, :units => volume)
- assert_equal '1.23 m3', number_to_human(1234567, :units => volume)
-
- #Including fractionals
- distance = {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"}
- assert_equal '1.23 mm', number_to_human(0.00123, :units => distance)
- assert_equal '1.23 cm', number_to_human(0.0123, :units => distance)
- assert_equal '1.23 dm', number_to_human(0.123, :units => distance)
- assert_equal '1.23 m', number_to_human(1.23, :units => distance)
- assert_equal '1.23 dam', number_to_human(12.3, :units => distance)
- assert_equal '1.23 hm', number_to_human(123, :units => distance)
- assert_equal '1.23 km', number_to_human(1230, :units => distance)
- assert_equal '1.23 km', number_to_human(1230, :units => distance)
- assert_equal '1.23 km', number_to_human(1230, :units => distance)
- assert_equal '12.3 km', number_to_human(12300, :units => distance)
-
- #The quantifiers don't need to be a continuous sequence
- gangster = {:hundred => "hundred bucks", :million => "thousand quids"}
- assert_equal '1 hundred bucks', number_to_human(100, :units => gangster)
- assert_equal '25 hundred bucks', number_to_human(2500, :units => gangster)
- assert_equal '25 thousand quids', number_to_human(25000000, :units => gangster)
- assert_equal '12300 thousand quids', number_to_human(12345000000, :units => gangster)
-
- #Spaces are stripped from the resulting string
- assert_equal '4', number_to_human(4, :units => {:unit => "", :ten => 'tens '})
- assert_equal '4.5 tens', number_to_human(45, :units => {:unit => "", :ten => ' tens '})
-
- assert_equal '1&lt;script&gt;&lt;/script&gt;01', number_to_human(1.01, :separator => "<script></script>")
- assert_equal '100&lt;script&gt;&lt;/script&gt;000 Quadrillion', number_to_human(10**20, :delimiter => "<script></script>")
+ assert_equal nil, number_to_human(nil)
+ assert_equal "0", number_to_human(0)
+ assert_equal "1.23 Thousand", number_to_human(1234)
+ assert_equal "489.0 Thousand", number_to_human(489000, precision: 4, strip_insignificant_zeros: false)
end
- def test_number_to_human_with_custom_format
- assert_equal '123 times Thousand', number_to_human(123456, :format => "%n times %u")
- volume = {:unit => "ml", :thousand => "lt", :million => "m3"}
- assert_equal '123.lt', number_to_human(123456, :units => volume, :format => "%n.%u")
- end
-
- def test_number_helpers_should_return_nil_when_given_nil
- assert_nil number_to_phone(nil)
- assert_nil number_to_currency(nil)
- assert_nil number_to_percentage(nil)
- assert_nil number_with_delimiter(nil)
- assert_nil number_with_precision(nil)
- assert_nil number_to_human_size(nil)
- assert_nil number_to_human(nil)
- end
+ def test_number_helpers_escape_delimiter_and_separator
+ assert_equal "111&lt;script&gt;&lt;/script&gt;111&lt;script&gt;&lt;/script&gt;1111", number_to_phone(1111111111, delimiter: "<script></script>")
- def test_number_helpers_do_not_mutate_options_hash
- options = { 'raise' => true }
+ assert_equal "$1&lt;script&gt;&lt;/script&gt;01", number_to_currency(1.01, separator: "<script></script>")
+ assert_equal "$1&lt;script&gt;&lt;/script&gt;000.00", number_to_currency(1000, delimiter: "<script></script>")
- number_to_phone(1, options)
- assert_equal({ 'raise' => true }, options)
+ assert_equal "1&lt;script&gt;&lt;/script&gt;010%", number_to_percentage(1.01, separator: "<script></script>")
+ assert_equal "1&lt;script&gt;&lt;/script&gt;000.000%", number_to_percentage(1000, delimiter: "<script></script>")
- number_to_currency(1, options)
- assert_equal({ 'raise' => true }, options)
+ assert_equal "1&lt;script&gt;&lt;/script&gt;01", number_with_delimiter(1.01, separator: "<script></script>")
+ assert_equal "1&lt;script&gt;&lt;/script&gt;000", number_with_delimiter(1000, delimiter: "<script></script>")
- number_to_percentage(1, options)
- assert_equal({ 'raise' => true }, options)
+ assert_equal "1&lt;script&gt;&lt;/script&gt;010", number_with_precision(1.01, separator: "<script></script>")
+ assert_equal "1&lt;script&gt;&lt;/script&gt;000.000", number_with_precision(1000, delimiter: "<script></script>")
- number_with_delimiter(1, options)
- assert_equal({ 'raise' => true }, options)
-
- number_with_precision(1, options)
- assert_equal({ 'raise' => true }, options)
-
- number_to_human_size(1, options)
- assert_equal({ 'raise' => true }, options)
-
- number_to_human(1, options)
- assert_equal({ 'raise' => true }, options)
- end
+ assert_equal "9&lt;script&gt;&lt;/script&gt;86 KB", number_to_human_size(10100, separator: "<script></script>")
- def test_number_helpers_should_return_non_numeric_param_unchanged
- assert_equal("+1-x x 123", number_to_phone("x", :country_code => 1, :extension => 123))
- assert_equal("x", number_to_phone("x"))
- assert_equal("$x.", number_to_currency("x."))
- assert_equal("$x", number_to_currency("x"))
- assert_equal("x%", number_to_percentage("x"))
- assert_equal("x", number_with_delimiter("x"))
- assert_equal("x.", number_with_precision("x."))
- assert_equal("x", number_with_precision("x"))
- assert_equal "x", number_to_human_size('x')
- assert_equal "x", number_to_human('x')
+ assert_equal "1&lt;script&gt;&lt;/script&gt;01", number_to_human(1.01, separator: "<script></script>")
+ assert_equal "100&lt;script&gt;&lt;/script&gt;000 Quadrillion", number_to_human(10**20, delimiter: "<script></script>")
end
def test_number_helpers_outputs_are_html_safe
@@ -332,8 +84,8 @@ class NumberHelperTest < ActionView::TestCase
assert number_to_human_size("asdf".html_safe).html_safe?
assert number_to_human_size("1".html_safe).html_safe?
- assert number_with_precision(1, :strip_insignificant_zeros => false).html_safe?
- assert number_with_precision(1, :strip_insignificant_zeros => true).html_safe?
+ assert number_with_precision(1, strip_insignificant_zeros: false).html_safe?
+ assert number_with_precision(1, strip_insignificant_zeros: true).html_safe?
assert !number_with_precision("<script></script>").html_safe?
assert number_with_precision("asdf".html_safe).html_safe?
assert number_with_precision("1".html_safe).html_safe?
@@ -362,37 +114,37 @@ class NumberHelperTest < ActionView::TestCase
def test_number_helpers_should_raise_error_if_invalid_when_specified
exception = assert_raise InvalidNumberError do
- number_to_human("x", :raise => true)
+ number_to_human("x", raise: true)
end
assert_equal "x", exception.number
exception = assert_raise InvalidNumberError do
- number_to_human_size("x", :raise => true)
+ number_to_human_size("x", raise: true)
end
assert_equal "x", exception.number
exception = assert_raise InvalidNumberError do
- number_with_precision("x", :raise => true)
+ number_with_precision("x", raise: true)
end
assert_equal "x", exception.number
exception = assert_raise InvalidNumberError do
- number_to_currency("x", :raise => true)
+ number_to_currency("x", raise: true)
end
assert_equal "x", exception.number
exception = assert_raise InvalidNumberError do
- number_to_percentage("x", :raise => true)
+ number_to_percentage("x", raise: true)
end
assert_equal "x", exception.number
exception = assert_raise InvalidNumberError do
- number_with_delimiter("x", :raise => true)
+ number_with_delimiter("x", raise: true)
end
assert_equal "x", exception.number
exception = assert_raise InvalidNumberError do
- number_to_phone("x", :raise => true)
+ number_to_phone("x", raise: true)
end
assert_equal "x", exception.number
end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 09e6ede064..1fe6dbd4d9 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,5 +1,8 @@
## Rails 4.0.0 (unreleased) ##
+
+## Rails 4.0.0.beta1 (February 25, 2013) ##
+
* Add `ActiveModel::Validations::AbsenceValidator`, a validator to check the
absence of attributes.
@@ -19,7 +22,7 @@
*Roberto Vasquez Angel*
-* `[attribute]_changed?` now returns `false` after a call to `reset_[attribute]!`
+* `[attribute]_changed?` now returns `false` after a call to `reset_[attribute]!`.
*Renato Mascarenhas*
@@ -27,9 +30,11 @@
*Rafael Mendonça França*
-* Specify type of singular association during serialization *Steve Klabnik*
+* Specify type of singular association during serialization.
+
+ *Steve Klabnik*
-* Fixed length validator to correctly handle nil values. Fixes #7180.
+* Fixed length validator to correctly handle `nil`. Fixes #7180.
*Michal Zima*
@@ -53,24 +58,27 @@
*Guillermo Iguaran*
-* Due to a change in builder, nil values and empty strings now generates
+* Due to a change in builder, `nil` and empty strings now generate
closed tags, so instead of this:
<pseudonyms nil=\"true\"></pseudonyms>
- It generates this:
+ it generates this:
<pseudonyms nil=\"true\"/>
*Carlos Antonio da Silva*
-* Changed inclusion and exclusion validators to accept a symbol for `:in` option.
+* Inclusion/exclusion validators accept a method name passed as a symbol to the
+ `:in` option.
- This allows to use dynamic inclusion/exclusion values using methods, besides the current lambda/proc support.
+ This allows to use dynamic inclusion/exclusion values using methods, besides
+ the current lambda/proc support.
*Gabriel Sobrinho*
-* `AM::Validation#validates` ability to pass custom exception to `:strict` option.
+* `ActiveModel::Validation#validates` ability to pass custom exception to the
+ `:strict` option.
*Bogdan Gusiev*
@@ -81,7 +89,7 @@
*Anthony Alberto*
-* Changed `AM::Serializers::JSON.include_root_in_json' default value to false.
+* Changed `ActiveModel::Serializers::JSON.include_root_in_json` default value to false.
Now, AM Serializers and AR objects have the same default behaviour. Fixes #6578.
class User < ActiveRecord::Base; end
@@ -108,16 +116,31 @@
*Francesco Rodriguez*
-* Passing false hash values to `validates` will no longer enable the corresponding validators *Steve Purcell*
+* Passing false hash values to `validates` will no longer enable the corresponding validators.
+
+ *Steve Purcell*
+
+* `ConfirmationValidator` error messages will attach to `:#{attribute}_confirmation` instead of `attribute`.
+
+ *Brian Cardarella*
-* `ConfirmationValidator` error messages will attach to `:#{attribute}_confirmation` instead of `attribute` *Brian Cardarella*
+* Added `ActiveModel::Model`, a mixin to make Ruby objects work with AP out of box.
+
+ *Guillermo Iguaran*
+
+* `AM::Errors#to_json`: support `:full_messages` parameter.
+
+ *Bogdan Gusiev*
-* Added ActiveModel::Model, a mixin to make Ruby objects work with AP out of box *Guillermo Iguaran*
+* Trim down Active Model API by removing `valid?` and `errors.full_messages`.
-* `AM::Errors#to_json`: support `:full_messages` parameter *Bogdan Gusiev*
+ *José Valim*
-* Trim down Active Model API by removing `valid?` and `errors.full_messages` *José Valim*
+* When `^` or `$` are used in the regular expression provided to `validates_format_of`
+ and the `:multiline` option is not set to true, an exception will be raised. This is
+ to prevent security vulnerabilities when using `validates_format_of`. The problem is
+ described in detail in the Rails security guide.
-* When `^` or `$` are used in the regular expression provided to `validates_format_of` and the :multiline option is not set to true, an exception will be raised. This is to prevent security vulnerabilities when using `validates_format_of`. The problem is described in detail in the Rails security guide *Jan Berdajs + Egor Homakov*
+ *Jan Berdajs + Egor Homakov*
Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activemodel/CHANGELOG.md) for previous changes.
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index f989b21140..037650e5ac 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -60,6 +60,9 @@ module ActiveModel
# end
# end
#
+ # Note that the validator is initialized only once for the whole application
+ # lifecycle, and not on each validation run.
+ #
# The easiest way to add custom validators for validating individual attributes
# is with the convenient <tt>ActiveModel::EachValidator</tt>.
#
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index e195c12a4d..7586b02037 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -3,7 +3,7 @@ module ActiveModel
MAJOR = 4
MINOR = 0
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index cc0c3f16d2..51dcfc37d8 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -274,6 +274,13 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.add_on_empty :name, :message => 'custom'
end
+ test "add_on_empty generates message with empty string value" do
+ person = Person.new
+ person.name = ''
+ person.errors.expects(:generate_message).with(:name, :empty, {})
+ person.errors.add_on_empty :name
+ end
+
test "add_on_blank generates message" do
person = Person.new
person.errors.expects(:generate_message).with(:name, :blank, {})
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index bb32d16fc1..4a5a2d17af 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,20 +1,221 @@
## Rails 4.0.0 (unreleased) ##
-* Fix calculation of `db_runtime` property in
+* Fix calculation of `db_runtime` property in
`ActiveRecord::Railties::ControllerRuntime#cleanup_view_runtime`.
- Previously, after raising `ActionView::MissingTemplate` db_runtime was
+ Previously, after raising `ActionView::MissingTemplate`, `db_runtime` was
not populated.
Fixes #9215.
*Igor Fedoronchuk*
+* Do not try to touch invalid (and thus not persisted) parent record
+ for a `belongs_to :parent, touch: true` association
+
+ *Olek Janiszewski*
+
+* Fix when performing an ordered join query. The bug only
+ affected queries where the order was given with a symbol.
+ Fixes #9275.
+
+ Example:
+
+ # This will expand the order :name to "authors".name.
+ Author.joins(:books).where('books.published = 1').order(:name)
+
+
+## Rails 4.0.0.beta1 (February 25, 2013) ##
+
+* Fix overriding of attributes by `default_scope` on `ActiveRecord::Base#dup`.
+
+ *Hiroshige UMINO*
+
+* Update queries now use prepared statements.
+
+ *Olli Rissanen*
+
+* Fixing issue #8345. Now throwing an error when one attempts to touch a
+ new object that has not yet been persisted. For instance:
+
+ Example:
+
+ ball = Ball.new
+ ball.touch :updated_at # => raises error
+
+ It is not until the ball object has been persisted that it can be touched.
+ This follows the behavior of update_column.
+
+ *John Wang*
+
+* Preloading ordered `has_many :through` associations no longer applies
+ invalid ordering to the `:through` association.
+ Fixes #8663.
+
+ *Yves Senn*
+
+* The auto explain feature has been removed. This feature was
+ activated by configuring `config.active_record.auto_explain_threshold_in_seconds`.
+ The configuration option was deprecated and has no more effect.
+
+ You can still use `ActiveRecord::Relation#explain` to see the EXPLAIN output for
+ any given relation.
+
+ *Yves Senn*
+
+* The `:on` option for `after_commit` and `after_rollback` now
+ accepts an Array of actions.
+ Fixes #988.
+
+ Example:
+
+ after_commit :update_cache on: [:create, :update]
+
+ *Yves Senn*
+
+* Rename related indexes on `rename_table` and `rename_column`. This
+ does not affect indexes with custom names.
+
+ *Yves Senn*
+
+* Prevent the creation of indices with too long names, which cause
+ internal operations to fail (sqlite3 adapter only). The method
+ `allowed_index_name_length` defines the length limit enforced by
+ rails. It's value defaults to `index_name_length` but can vary per adapter.
+ Fixes #8264.
+
+ *Yves Senn*
+
+* Fixing issue #776.
+
+ Memory bloat in transactions is handled by having the transaction hold only
+ the AR objects which it absolutely needs to know about. These are the AR
+ objects with callbacks (they need to be updated as soon as something in the
+ transaction occurs).
+
+ All other AR objects can be updated lazily by keeping a reference to a
+ TransactionState object. If an AR object gets inside a transaction, then
+ the transaction will add its TransactionState to the AR object. When the
+ user makes a call to some attribute on an AR object (which has no
+ callbacks) associated with a transaction, the AR object will call the
+ sync_with_transaction_state method and make sure it is up to date with the
+ transaction. After it has synced with the transaction state, the AR object
+ will return the attribute that was requested.
+
+ Most of the logic in the changes are used to handle multiple transactions,
+ in which case the AR object has to recursively follow parent pointers of
+ TransactionState objects.
+
+ *John Wang*
+
+* Descriptive error message when the necessary AR adapter gem was not found.
+ Fixes #7313.
+
+ *Yves Senn*
+
+* Active Record now raises an error when blank arguments are passed to query
+ methods for which blank arguments do not make sense.
+
+ Example:
+
+ Post.includes() # => raises error
+
+ *John Wang*
+
+* Simplified type casting code for timezone aware attributes to use the
+ `in_time_zone` method if it is available. This introduces a subtle change
+ of behavior when using `Date` instances as they are directly converted to
+ `ActiveSupport::TimeWithZone` instances without first being converted to
+ `Time` instances. For example:
+
+ # Rails 3.2 behavior
+ >> Date.today.to_time.in_time_zone
+ => Wed, 13 Feb 2013 07:00:00 UTC +00:00
+
+ # Rails 4.0 behavior
+ >> Date.today.in_time_zone
+ => Wed, 13 Feb 2013 00:00:00 UTC +00:00
+
+ On the plus side it now behaves the same whether you pass a `String` date
+ or an actual `Date` instance. For example:
+
+ # Rails 3.2 behavior
+ >> Date.civil(2013, 2, 13).to_time.in_time_zone
+ => Wed, 13 Feb 2013 07:00:00 UTC +00:00
+ >> Time.zone.parse("2013-02-13")
+ => Wed, 13 Feb 2013 00:00:00 UTC +00:00
+
+ # Rails 4.0 behavior
+ >> Date.civil(2013, 2, 13).in_time_zone
+ => Wed, 13 Feb 2013 00:00:00 UTC +00:00
+ >> "2013-02-13".in_time_zone
+ => Wed, 13 Feb 2013 00:00:00 UTC +00:00
+
+ If you need the old behavior you can convert the dates to times manually.
+ For example:
+
+ >> Post.new(created_at: Date.today).created_at
+ => Wed, 13 Feb 2013 00:00:00 UTC +00:00
+
+ >> Post.new(created_at: Date.today.to_time).created_at
+ => Wed, 13 Feb 2013 07:00:00 UTC +00:00
+
+ *Andrew White*
+
+* Preloading `has_many :through` associations with conditions won't
+ cache the `:through` association. This will prevent invalid
+ subsets to be cached.
+ Fixes #8423.
+
+ Example:
+
+ class User
+ has_many :posts
+ has_many :recent_comments, -> { where('created_at > ?', 1.week.ago) }, :through => :posts
+ end
+
+ a_user = User.includes(:recent_comments).first
+
+ # This is preloaded.
+ a_user.recent_comments
+
+ # This is not preloaded, fetched now.
+ a_user.posts
+
+ *Yves Senn*
+
+* Don't run `after_commit` callbacks when creating through an association
+ if saving the record fails.
+
+ *James Miller*
+
+* Allow store accessors to be overrided like other attribute methods, e.g.:
+
+ class User < ActiveRecord::Base
+ store :settings, accessors: [ :color, :homepage ], coder: JSON
+
+ def color
+ super || 'red'
+ end
+ end
+
+ *Sergey Nartimov*
+
+* Quote numeric values being compared to non-numeric columns. Otherwise,
+ in some database, the string column values will be coerced to a numeric
+ allowing 0, 0.0 or false to match any string starting with a non-digit.
+
+ Example:
+
+ App.where(apikey: 0) # => SELECT * FROM users WHERE apikey = '0'
+
+ *Dylan Smith*
+
* Schema dumper supports dumping the enabled database extensions to `schema.rb`
(currently only supported by postgresql).
*Justin George*
-* The `DATABASE_URL` environment variable now converts ints, floats, and
- the strings true and false to Ruby types. For example, SQLite requires
+* The database adpters now converts the options passed thought `DATABASE_URL`
+ environment variable to the proper Ruby types before using. For example, SQLite requires
that the timeout value is an integer, and PostgreSQL requires that the
prepared_statements option is a boolean. These now work as expected:
@@ -23,7 +224,7 @@
DATABASE_URL=sqlite3://localhost/test_db?timeout=500
DATABASE_URL=postgresql://localhost/test_db?prepared_statements=false
- *Aaron Stone*
+ *Aaron Stone + Rafael Mendonça França*
* `Relation#merge` now only overwrites where values on the LHS of the
merge. Consider:
@@ -80,7 +281,7 @@
*Lilibeth De La Cruz*
* When `#count` is used in conjunction with `#uniq` we perform `count(:distinct => true)`.
- Fix #6865.
+ Fixes #6865.
Example:
@@ -113,7 +314,7 @@
*John Wang*
* Collection associations `#empty?` always respects builded records.
- Fix #8879.
+ Fixes #8879.
Example:
@@ -123,10 +324,6 @@
*Yves Senn*
-* Remove support for parsing YAML parameters from request.
-
- *Aaron Patterson*
-
* Support for PostgreSQL's `ltree` data type.
*Rob Worley*
@@ -152,7 +349,7 @@
* Improve ways to write `change` migrations, making the old `up` & `down` methods no longer necessary.
* The methods `drop_table` and `remove_column` are now reversible, as long as the necessary information is given.
- The method `remove_column` used to accept multiple column names; instead use `remove_columns` (which is not revertible).
+ The method `remove_column` used to accept multiple column names; instead use `remove_columns` (which is not reversible).
The method `change_table` is also reversible, as long as its block doesn't call `remove`, `change` or `change_default`
* New method `reversible` makes it possible to specify code to be run when migrating up or down.
@@ -206,18 +403,13 @@
*Yves Senn*
* Add `ActiveRecord::Base.cache_timestamp_format` class attribute to control
- the format of the timestamp value in the cache key.
- This allows users to improve the precision of the cache key.
+ the format of the timestamp value in the cache key. Defaults to `:nsec`.
Fixes #8195.
*Rafael Mendonça França*
-* Add `:nsec` date format. This can be used to improve the precision of cache key.
-
- *Jamie Gaskins*
-
* Session variables can be set for the `mysql`, `mysql2`, and `postgresql` adapters
- in the `variables: <hash>` parameter in `database.yml`. The key-value pairs of this
+ in the `variables: <hash>` parameter in `config/database.yml`. The key-value pairs of this
hash will be sent in a `SET key = value` query on new database connections. See also:
http://dev.mysql.com/doc/refman/5.0/en/set-statement.html
http://www.postgresql.org/docs/8.3/static/sql-set.html
@@ -240,7 +432,7 @@
to the update query.
class User < ActiveRecord::Base
- default_scope where(active: true)
+ default_scope -> { where(active: true) }
end
user = User.first
@@ -377,11 +569,6 @@
*kennyj*
-* Added `#none!` method for mutating `ActiveRecord::Relation` objects to a NullRelation.
- It acts like `#none` but modifies relation in place.
-
- *Juanjo Bazán*
-
* Fix bug where `update_columns` and `update_column` would not let you update the primary key column.
*Henrik Nyh*
@@ -604,7 +791,7 @@
After:
- #=> SELECT * FROM users WHERE 1 = 2;
+ #=> SELECT * FROM users WHERE 1=0;
*Damien Mathieu*
@@ -1098,12 +1285,11 @@
* `:conditions` becomes `:where`.
* `:include` becomes `:includes`.
- The code to implement the deprecated features has been moved out to
- the `activerecord-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.
+ The code to implement the deprecated features has been moved out to the
+ `activerecord-deprecated_finders` gem. This gem is a dependency of Active
+ Record in Rails 4.0, so the interface works out of the box. It will no
+ longer be a dependency from Rails 4.1 (you'll need to add it to the
+ `Gemfile` in 4.1), and will be maintained until Rails 5.0.
*Jon Leighton*
@@ -1159,13 +1345,6 @@
*Joshua Wood*
-* 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
-
- *Jon Leighton*
-
* Added `#find_by` and `#find_by!` to mirror the functionality
provided by dynamic finders in a way that allows dynamic input more
easily:
diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc
index bdd8834dcb..2f3d516c43 100644
--- a/activerecord/RUNNING_UNIT_TESTS.rdoc
+++ b/activerecord/RUNNING_UNIT_TESTS.rdoc
@@ -2,7 +2,7 @@
If you don't have the environment set make sure to read
- http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#testing-active-record.
+ http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#testing-active-record
== Running the Tests
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index d523c1eca1..89a62f0873 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -24,6 +24,6 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
s.add_dependency 'activemodel', version
- s.add_dependency 'arel', '~> 3.0.2'
+ s.add_dependency 'arel', '~> 4.0.0.beta1'
s.add_dependency 'activerecord-deprecated_finders', '~> 0.0.3'
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 06bdabfced..513d1012ba 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -965,8 +965,8 @@ module ActiveRecord
# For +has_and_belongs_to_many+, <tt>delete</tt> and <tt>destroy</tt> are the same: they
# cause the records in the join table to be removed.
#
- # For +has_many+, <tt>destroy</tt> will always call the <tt>destroy</tt> method of the
- # record(s) being removed so that callbacks are run. However <tt>delete</tt> will either
+ # For +has_many+, <tt>destroy</tt> and <tt>destory_all</tt> will always call the <tt>destroy</tt> method of the
+ # record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
# do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
# if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
# The default strategy is <tt>:nullify</tt> (set the foreign keys to <tt>nil</tt>), except for
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 3f0e4ca999..868095f068 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -146,7 +146,7 @@ module ActiveRecord
def interpolate(sql, record = nil)
if sql.respond_to?(:to_proc)
- owner.send(:instance_exec, record, &sql)
+ owner.instance_exec(record, &sql)
else
sql
end
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 532af3e83e..54b1a69774 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -1,5 +1,5 @@
module ActiveRecord
- # = Active Record Belongs To Associations
+ # = Active Record Belongs To Association
module Associations
class BelongsToAssociation < SingularAssociation #:nodoc:
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 2f2600b7fb..97b1ff18e2 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -48,7 +48,7 @@ module ActiveRecord::Associations::Builder
def belongs_to_touch_after_save_or_destroy_for_#{name}
record = #{name}
- unless record.nil?
+ unless record.nil? || record.new_record?
record.touch #{options[:touch].inspect if options[:touch] != true}
end
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
index 711f7b3ce1..5604687b57 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
@@ -70,7 +70,7 @@ module ActiveRecord
end
def instantiate(row)
- @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
+ @cached_record[record_id(row)] ||= active_record.instantiate(extract_record(row))
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index 1c1ba11c44..c4b50ab306 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -31,7 +31,8 @@ module ActiveRecord
through_records = Array.wrap(owner.send(through_reflection.name))
# Dont cache the association - we would only be caching a subset
- if reflection.options[:source_type] && through_reflection.collection?
+ if (through_scope != through_reflection.klass.unscoped) ||
+ (reflection.options[:source_type] && through_reflection.collection?)
owner.association(through_reflection.name).reset
end
@@ -46,12 +47,12 @@ module ActiveRecord
through_scope.where! reflection.foreign_type => options[:source_type]
else
unless reflection_scope.where_values.empty?
- through_scope.includes_values = reflection_scope.values[:includes] || options[:source]
+ through_scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
through_scope.where_values = reflection_scope.values[:where]
end
- through_scope.order! reflection_scope.values[:order]
through_scope.references! reflection_scope.values[:references]
+ through_scope.order! reflection_scope.values[:order] if through_scope.eager_loading?
end
through_scope
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 310f1b6e75..3e454b713a 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -8,27 +8,32 @@ module ActiveRecord
# Returns this record's primary key value wrapped in an Array if one is
# available.
def to_key
+ sync_with_transaction_state
key = self.id
[key] if key
end
# Returns the primary key value.
def id
+ sync_with_transaction_state
read_attribute(self.class.primary_key)
end
# Sets the primary key value.
def id=(value)
+ sync_with_transaction_state
write_attribute(self.class.primary_key, value) if self.class.primary_key
end
# Queries the primary key value.
def id?
+ sync_with_transaction_state
query_attribute(self.class.primary_key)
end
# Returns the primary key value before type cast.
def id_before_type_cast
+ sync_with_transaction_state
read_attribute_before_type_cast(self.class.primary_key)
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 3c03cce838..506f5d75f9 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -74,7 +74,7 @@ module ActiveRecord
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
# If it's cached, just return it
- # We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/3552829.
+ # We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
name = attr_name.to_s
@attributes_cache[name] || @attributes_cache.fetch(name) {
column = @columns_hash.fetch(name) {
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 704998301c..55542262b0 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -347,7 +347,7 @@ module ActiveRecord
end
# reconstruct the scope now that we know the owner's id
- association.send(:reset_scope) if association.respond_to?(:reset_scope)
+ association.reset_scope if association.respond_to?(:reset_scope)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 847d9da6e6..1754e424b8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -577,10 +577,10 @@ module ActiveRecord
# When a connection is established or removed, we invalidate the cache.
#
# Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
- # However, benchmarking (https://gist.github.com/3552829) showed that #fetch is
- # significantly slower than #[]. So in the nil case, no caching will take place,
- # but that's ok since the nil case is not the common one that we wish to optimise
- # for.
+ # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
+ # #fetch is significantly slower than #[]. So in the nil case, no caching will
+ # take place, but that's ok since the nil case is not the common one that we wish
+ # to optimise for.
def retrieve_connection_pool(klass)
class_to_pool[klass.name] ||= begin
until pool = pool_for(klass)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index 30ccb8f0a4..2859fb31e8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -17,6 +17,15 @@ module ActiveRecord
64
end
+ # Returns the maximum allowed length for an index name. This
+ # limit is enforced by rails and Is less than or equal to
+ # <tt>index_name_length</tt>. The gap between
+ # <tt>index_name_length</tt> is to allow internal rails
+ # opreations to use prefixes in temporary opreations.
+ def allowed_index_name_length
+ index_name_length
+ end
+
# Returns the maximum length of an index name.
def index_name_length
64
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index d18b9c991f..aec4654eee 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -25,13 +25,19 @@ module ActiveRecord
when true, false
if column && column.type == :integer
value ? '1' : '0'
+ elsif column && [:text, :string, :binary].include?(column.type)
+ value ? "'1'" : "'0'"
else
value ? quoted_true : quoted_false
end
# BigDecimals need to be put in a non-normalized form and quoted.
when nil then "NULL"
- when BigDecimal then value.to_s('F')
- when Numeric, ActiveSupport::Duration then value.to_s
+ when Numeric, ActiveSupport::Duration
+ value = BigDecimal === value ? value.to_s('F') : value.to_s
+ if column && ![:integer, :float, :decimal].include?(column.type)
+ value = "'#{value}'"
+ end
+ value
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
when Class then "'#{value.to_s}'"
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 b1ec33d06c..f758e19a4f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -252,15 +252,12 @@ module ActiveRecord
self
end
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV, __FILE__, __LINE__ + 1
- def #{column_type}(*args) # def string(*args)
- options = args.extract_options! # options = args.extract_options!
- column_names = args # column_names = args
- type = :'#{column_type}' # type = :string
- column_names.each { |name| column(name, type, options) } # column_names.each { |name| column(name, type, options) }
- end # end
- EOV
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
+ define_method column_type do |*args|
+ options = args.extract_options!
+ column_names = args
+ column_names.each { |name| column(name, column_type, options) }
+ end
end
# Adds index options to the indexes hash, keyed by column name
@@ -486,15 +483,13 @@ module ActiveRecord
#
# t.string(:goat)
# t.string(:goat, :sheep)
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV, __FILE__, __LINE__ + 1
- def #{column_type}(*args) # def string(*args)
- options = args.extract_options! # options = args.extract_options!
- args.each do |name| # column_names.each do |name|
- @base.add_column(@table_name, name, :#{column_type}, options) # @base.add_column(@table_name, name, :string, options)
- end # end
- end # end
- EOV
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
+ define_method column_type do |*args|
+ options = args.extract_options!
+ args.each do |name|
+ @base.add_column(@table_name, name, column_type, options)
+ end
+ end
end
private
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index fd5eaab9c9..f587bf8140 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -1,10 +1,12 @@
+require 'ipaddr'
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
# The goal of this module is to move Adapter specific column
# definitions to the Adapter instead of having it in the schema
# dumper itself. This code represents the normal case.
# We can then redefine how certain data types may be handled in the schema dumper on the
- # Adapter level by over-writing this code inside the database spececific adapters
+ # Adapter level by over-writing this code inside the database specific adapters
module ColumnDumper
def column_spec(column, types)
spec = prepare_column_options(column, types)
@@ -50,6 +52,15 @@ module ActiveRecord
when Range
# infinity dumps as Infinity, which causes uninitialized constant error
value.inspect.gsub('Infinity', '::Float::INFINITY')
+ when IPAddr
+ subnet_mask = value.instance_variable_get(:@mask_addr)
+
+ # If the subnet mask is equal to /32, don't output it
+ if subnet_mask == (2**32 - 1)
+ "\"#{value.to_s}\""
+ else
+ "\"#{value.to_s}/#{subnet_mask.to_s(2).count('1')}\""
+ end
else
value.inspect
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index cdc8433185..9bae880024 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -192,6 +192,14 @@ module ActiveRecord
# Set to true to drop the table before creating it.
# Defaults to false.
#
+ # Note that +create_join_table+ does not create any indices by default; you can use
+ # its block form to do so yourself:
+ #
+ # create_join_table :products, :categories do |t|
+ # t.index :products
+ # t.index :categories
+ # end
+ #
# ====== Add a backend specific option to the generated SQL (MySQL)
# create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
# generates:
@@ -647,10 +655,11 @@ module ActiveRecord
index_name = index_name(table_name, column: column_names)
if Hash === options # legacy support, since this param was a string
- options.assert_valid_keys(:unique, :order, :name, :where, :length)
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal)
index_type = options[:unique] ? "UNIQUE" : ""
index_name = options[:name].to_s if options.key?(:name)
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
if supports_partial_index?
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
@@ -665,10 +674,11 @@ module ActiveRecord
end
index_type = options
+ max_index_length = allowed_index_name_length
end
- if index_name.length > index_name_length
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
+ if index_name.length > max_index_length
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
end
if index_name_exists?(table_name, index_name, false)
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
@@ -694,6 +704,28 @@ module ActiveRecord
column_names.map {|column_name| quote_column_name(column_name) }
end
+ def rename_table_indexes(table_name, new_name)
+ indexes(new_name).each do |index|
+ generated_index_name = index_name(table_name, column: index.columns)
+ if generated_index_name == index.name
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
+ end
+ end
+ end
+
+ def rename_column_indexes(table_name, column_name, new_column_name)
+ column_name, new_column_name = column_name.to_s, new_column_name.to_s
+ indexes(table_name).each do |index|
+ next unless index.columns.include?(new_column_name)
+ old_columns = index.columns.dup
+ old_columns[old_columns.index(new_column_name)] = column_name
+ generated_index_name = index_name(table_name, column: old_columns)
+ if generated_index_name == index.name
+ rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
+ end
+ end
+ end
+
private
def table_definition
TableDefinition.new(self)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 3ecef96b10..73c80a3220 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -5,7 +5,7 @@ module ActiveRecord
def initialize(connection)
@connection = connection
- @state = TransactionState.new
+ @state = TransactionState.new
end
def state
@@ -14,11 +14,13 @@ module ActiveRecord
end
class TransactionState
+ attr_accessor :parent
VALID_STATES = Set.new([:committed, :rolledback, nil])
def initialize(state = nil)
@state = state
+ @parent = nil
end
def committed?
@@ -116,7 +118,11 @@ module ActiveRecord
end
def add_record(record)
- records << record
+ if record.has_transactional_callbacks?
+ records << record
+ else
+ record.set_transaction_state(@state)
+ end
end
def rollback_records
@@ -188,8 +194,9 @@ module ActiveRecord
end
def perform_commit
+ @state.set_state(:committed)
+ @state.parent = parent.state
connection.release_savepoint
- records.each { |r| parent.add_record(r) }
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 26f601bf05..ff9de712bc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -61,12 +61,30 @@ module ActiveRecord
include MonitorMixin
include ColumnDumper
+ SIMPLE_INT = /\A\d+\z/
+
define_callbacks :checkout, :checkin
attr_accessor :visitor, :pool
attr_reader :schema_cache, :last_use, :in_use, :logger
alias :in_use? :in_use
+ def self.type_cast_config_to_integer(config)
+ if config =~ SIMPLE_INT
+ config.to_i
+ else
+ config
+ end
+ end
+
+ def self.type_cast_config_to_boolean(config)
+ if config == "false"
+ false
+ else
+ config
+ end
+ end
+
def initialize(connection, logger = nil, pool = nil) #:nodoc:
super()
@@ -171,14 +189,14 @@ module ActiveRecord
false
end
- # Does this adapter support database extensions? As of this writing
- # only postgresql does.
+ # Does this adapter support database extensions? As of this writing only
+ # postgresql does.
def supports_extensions?
false
end
- # A list of extensions, to be filled in by databases that
- # support them (at the moment, postgresql).
+ # A list of extensions, to be filled in by adapters that support them. At
+ # the moment only postgresql does.
def extensions
[]
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 52b0b3fe79..5480204511 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -31,7 +31,7 @@ module ActiveRecord
return false if blob_or_text_column? #mysql forbids defaults on blob and text columns
super
end
-
+
def blob_or_text_column?
sql_type =~ /blob/i || type == :text
end
@@ -140,7 +140,7 @@ module ActiveRecord
@connection_options, @config = connection_options, config
@quoted_column_names, @quoted_table_names = {}, {}
- if config.fetch(:prepared_statements) { true }
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@visitor = Arel::Visitors::MySQL.new self
else
@visitor = BindSubstitution.new self
@@ -212,8 +212,6 @@ module ActiveRecord
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
s = column.class.string_to_binary(value).unpack("H*")[0]
"x'#{s}'"
- elsif value.kind_of?(BigDecimal)
- value.to_s("F")
else
super
end
@@ -470,6 +468,7 @@ module ActiveRecord
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
+ rename_table_indexes(table_name, new_name)
end
def add_column(table_name, column_name, type, options = {})
@@ -497,6 +496,7 @@ module ActiveRecord
def rename_column(table_name, column_name, new_column_name) #:nodoc:
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
+ rename_column_indexes(table_name, column_name, new_column_name)
end
# Maps logical Rails types to MySQL-specific data types.
@@ -581,7 +581,7 @@ module ActiveRecord
end
def strict_mode?
- @config.fetch(:strict, true)
+ self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
end
protected
@@ -722,7 +722,7 @@ module ActiveRecord
# Increase timeout so the server doesn't disconnect us.
wait_timeout = @config[:wait_timeout]
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
- variables[:wait_timeout] = wait_timeout
+ variables[:wait_timeout] = self.class.type_cast_config_to_integer(wait_timeout)
# Make MySQL reject illegal values rather than truncating or blanking them, see
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
@@ -749,7 +749,6 @@ module ActiveRecord
# ...and send them all in one query
execute("SET #{encoding} #{variable_assignments}", :skip_logging)
end
-
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 747331f3a1..a4b3a0c584 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -74,12 +74,13 @@ module ActiveRecord
def type_cast_for_write(value)
return value unless number?
- if value == false
+ case value
+ when FalseClass
0
- elsif value == true
+ when TrueClass
1
- elsif value.is_a?(String) && value.blank?
- nil
+ when String
+ value.presence
else
value
end
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 577a362568..2c683fc3ac 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -38,7 +38,7 @@ module ActiveRecord
private
def resolve_string_connection(spec) # :nodoc:
hash = configurations.fetch(spec) do |k|
- connection_url_to_hash(k)
+ self.class.connection_url_to_hash(k)
end
raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
@@ -51,10 +51,13 @@ module ActiveRecord
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
+ path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
begin
- require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
+ require path_to_adapter
+ rescue Gem::LoadError => e
+ raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile."
rescue LoadError => e
- raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace
+ raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
end
adapter_method = "#{spec[:adapter]}_connection"
@@ -62,11 +65,7 @@ module ActiveRecord
ConnectionSpecification.new(spec, adapter_method)
end
- # For DATABASE_URL, accept a limited concept of ints and floats
- SIMPLE_INT = /\A\d+\z/
- SIMPLE_FLOAT = /\A\d+\.\d+\z/
-
- def connection_url_to_hash(url) # :nodoc:
+ def self.connection_url_to_hash(url) # :nodoc:
config = URI.parse url
adapter = config.scheme
adapter = "postgresql" if adapter == "postgres"
@@ -86,28 +85,11 @@ module ActiveRecord
if config.query
options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
- options.each { |key, value| options[key] = type_cast_value(value) }
-
spec.merge!(options)
end
spec
end
-
- def type_cast_value(value)
- case value
- when SIMPLE_INT
- value.to_i
- when SIMPLE_FLOAT
- value.to_f
- when 'true'
- true
- when 'false'
- false
- else
- value
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 631f646f58..7544c2a783 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -126,7 +126,7 @@ module ActiveRecord
def initialize(connection, logger, connection_options, config)
super
@statements = StatementPool.new(@connection,
- config.fetch(:statement_limit) { 1000 })
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
@client_encoding = nil
connect
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 73ca2c8e61..3bc61c5e0c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -321,14 +321,16 @@ module ActiveRecord
#
# Example:
# rename_table('octopuses', 'octopi')
- def rename_table(name, new_name)
+ def rename_table(table_name, new_name)
clear_cache!
- execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
pk, seq = pk_and_sequence_for(new_name)
- if seq == "#{name}_#{pk}_seq"
+ if seq == "#{table_name}_#{pk}_seq"
new_seq = "#{new_name}_#{pk}_seq"
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
end
+
+ rename_table_indexes(table_name, new_name)
end
# Adds a new column to the named table.
@@ -370,6 +372,7 @@ module ActiveRecord
def rename_column(table_name, column_name, new_column_name)
clear_cache!
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
+ rename_column_indexes(table_name, column_name, new_column_name)
end
def remove_index!(table_name, index_name) #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 0818760b11..2bb2557efd 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -478,7 +478,7 @@ module ActiveRecord
def initialize(connection, logger, connection_parameters, config)
super(connection, logger)
- if config.fetch(:prepared_statements) { true }
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@visitor = Arel::Visitors::PostgreSQL.new self
else
@visitor = BindSubstitution.new self
@@ -492,7 +492,7 @@ module ActiveRecord
connect
@statements = StatementPool.new @connection,
- config.fetch(:statement_limit) { 1000 }
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
if postgresql_version < 80200
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
@@ -500,7 +500,7 @@ module ActiveRecord
initialize_type_map
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
- @use_insert_returning = @config.key?(:insert_returning) ? @config[:insert_returning] : true
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end
# Clears the prepared statements cache.
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 7bead4bde9..981c4c96a0 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -26,7 +26,7 @@ module ActiveRecord
:results_as_hash => true
)
- db.busy_timeout(config[:timeout]) if config[:timeout]
+ db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
end
@@ -107,10 +107,10 @@ module ActiveRecord
@active = nil
@statements = StatementPool.new(@connection,
- config.fetch(:statement_limit) { 1000 })
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
@config = config
- if config.fetch(:prepared_statements) { true }
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@visitor = Arel::Visitors::SQLite.new self
else
@visitor = BindSubstitution.new self
@@ -187,6 +187,13 @@ module ActiveRecord
true
end
+ # Returns 62. SQLite supports index names up to 64
+ # characters. The rest is used by rails internally to perform
+ # temporary rename operations
+ def allowed_index_name_length
+ index_name_length - 2
+ end
+
def native_database_types #:nodoc:
{
:primary_key => default_primary_key_type,
@@ -428,8 +435,9 @@ module ActiveRecord
#
# Example:
# rename_table('octopuses', 'octopi')
- def rename_table(name, new_name)
- exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
+ def rename_table(table_name, new_name)
+ exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
+ rename_table_indexes(table_name, new_name)
end
# See: http://www.sqlite.org/lang_altertable.html
@@ -488,6 +496,7 @@ module ActiveRecord
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
end
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
+ rename_column_indexes(table_name, column_name, new_column_name)
end
protected
@@ -502,7 +511,7 @@ module ActiveRecord
end
def alter_table(table_name, options = {}) #:nodoc:
- altered_table_name = "altered_#{table_name}"
+ altered_table_name = "a#{table_name}"
caller = lambda {|definition| yield definition if block_given?}
transaction do
@@ -546,10 +555,10 @@ module ActiveRecord
def copy_table_indexes(from, to, rename = {}) #:nodoc:
indexes(from).each do |index|
name = index.name
- if to == "altered_#{from}"
- name = "temp_#{name}"
- elsif from == "altered_#{to}"
- name = name[5..-1]
+ if to == "a#{from}"
+ name = "t#{name}"
+ elsif from == "a#{to}"
+ name = name[1..-1]
end
to_column_names = columns(to).map { |c| c.name }
@@ -559,7 +568,7 @@ module ActiveRecord
unless columns.empty?
# index name can't be the same
- opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_") }
+ opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
opts[:unique] = true if index.unique
add_index(to, columns, opts)
end
@@ -602,7 +611,6 @@ module ActiveRecord
super
end
end
-
end
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 63a1197a56..899fe7d7c7 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -249,7 +249,6 @@ module ActiveRecord
@new_record = true
ensure_proper_type
- populate_with_current_scope_attributes
super
end
@@ -347,8 +346,54 @@ module ActiveRecord
Hash[methods.map { |method| [method, public_send(method)] }].with_indifferent_access
end
+ def set_transaction_state(state) # :nodoc:
+ @transaction_state = state
+ end
+
+ def has_transactional_callbacks? # :nodoc:
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_create_callbacks.empty?
+ end
+
private
+ # Updates the attributes on this particular ActiveRecord object so that
+ # if it is associated with a transaction, then the state of the AR object
+ # will be updated to reflect the current state of the transaction
+ #
+ # The @transaction_state variable stores the states of the associated
+ # transaction. This relies on the fact that a transaction can only be in
+ # one rollback or commit (otherwise a list of states would be required)
+ # Each AR object inside of a transaction carries that transaction's
+ # TransactionState.
+ #
+ # This method checks to see if the ActiveRecord object's state reflects
+ # the TransactionState, and rolls back or commits the ActiveRecord object
+ # as appropriate.
+ #
+ # Since ActiveRecord objects can be inside multiple transactions, this
+ # method recursively goes through the parent of the TransactionState and
+ # checks if the ActiveRecord object reflects the state of the object.
+ def sync_with_transaction_state
+ update_attributes_from_transaction_state(@transaction_state, 0)
+ end
+
+ def update_attributes_from_transaction_state(transaction_state, depth)
+ if transaction_state && !has_transactional_callbacks?
+ unless @reflects_state[depth]
+ if transaction_state.committed?
+ committed!
+ elsif transaction_state.rolledback?
+ rolledback!
+ end
+ @reflects_state[depth] = true
+ end
+
+ if transaction_state.parent && !@reflects_state[depth+1]
+ update_attributes_from_transaction_state(transaction_state.parent, depth+1)
+ end
+ end
+ end
+
# Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
# of the array, and then rescues from the possible NoMethodError. If those elements are
# ActiveRecord::Base's, then this triggers the various method_missing's that we have,
@@ -376,7 +421,8 @@ module ActiveRecord
@new_record = true
@txn = nil
@_start_transaction_state = {}
- @transaction = nil
+ @transaction_state = nil
+ @reflects_state = [false]
end
end
end
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index 70683eb731..b2a9a54af1 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -2,43 +2,7 @@ require 'active_support/lazy_load_hooks'
module ActiveRecord
module Explain
- def self.extended(base)
- base.mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false
- end
-
- # If the database adapter supports explain and auto explain is enabled,
- # this method triggers EXPLAIN logging for the queries triggered by the
- # block if it takes more than the threshold as a whole. That is, the
- # threshold is not checked against each individual query, but against the
- # duration of the entire block. This approach is convenient for relations.
-
- #
- # The available_queries_for_explain thread variable collects the queries
- # to be explained. If the value is nil, it means queries are not being
- # currently collected. A false value indicates collecting is turned
- # off. Otherwise it is an array of queries.
- def logging_query_plan # :nodoc:
- return yield unless logger
-
- threshold = auto_explain_threshold_in_seconds
- current = Thread.current
- if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
- begin
- queries = current[:available_queries_for_explain] = []
- start = Time.now
- result = yield
- logger.warn(exec_explain(queries)) if Time.now - start > threshold
- result
- ensure
- current[:available_queries_for_explain] = nil
- end
- else
- yield
- end
- end
-
- # Relation#explain needs to be able to collect the queries regardless of
- # whether auto explain is enabled. This method serves that purpose.
+ # Relation#explain needs to be able to collect the queries.
def collecting_queries_for_explain # :nodoc:
current = Thread.current
original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
@@ -68,20 +32,5 @@ module ActiveRecord
end
str
end
-
- # Silences automatic EXPLAIN logging for the duration of the block.
- #
- # This has high priority, no EXPLAINs will be run even if downwards
- # the threshold is set to 0.
- #
- # As the name of the method suggests this only applies to automatic
- # EXPLAINs, manual calls to <tt>ActiveRecord::Relation#explain</tt> run.
- def silence_auto_explain
- current = Thread.current
- original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
- yield
- ensure
- current[:available_queries_for_explain] = original
- end
end
end
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index 7f877a6471..32d35f0ec1 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -5,8 +5,10 @@ module ActiveRecord
included do
##
# :singleton-method:
- # Indicates the format used to generate the timestamp format in the cache key.
- # This is +:number+, by default.
+ # Indicates the format used to generate the timestamp in the cache key.
+ # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
+ #
+ # This is +:nsec+, by default.
class_attribute :cache_timestamp_format, :instance_writer => false
self.cache_timestamp_format = :nsec
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 803cae7115..347f023793 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -69,11 +69,13 @@ module ActiveRecord
# Returns true if this object hasn't been saved yet -- that is, a record
# for the object doesn't exist in the data store yet; otherwise, returns false.
def new_record?
+ sync_with_transaction_state
@new_record
end
# Returns true if this object has been destroyed, otherwise returns false.
def destroyed?
+ sync_with_transaction_state
@destroyed
end
@@ -365,7 +367,16 @@ module ActiveRecord
#
# # triggers @brake.car.touch and @brake.car.corporation.touch
# @brake.touch
+ #
+ # Note that +touch+ must be used on a persisted object, or else an
+ # ActiveRecordError will be thrown. For example:
+ #
+ # ball = Ball.new
+ # ball.touch(:updated_at) # => raises ActiveRecordError
+ #
def touch(name = nil)
+ raise ActiveRecordError, "can not touch on a new record object" unless persisted?
+
attributes = timestamp_attributes_for_update_in_model
attributes << name if name
@@ -418,13 +429,22 @@ module ActiveRecord
# Returns the number of affected rows.
def update_record(attribute_names = @attributes.keys)
attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
-
if attributes_with_values.empty?
0
else
klass = self.class
- stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
- klass.connection.update stmt
+ column_hash = klass.connection.schema_cache.columns_hash klass.table_name
+ db_columns_with_values = attributes_with_values.map { |attr,value|
+ real_column = column_hash[attr.name]
+ [real_column, value]
+ }
+ bind_attrs = attributes_with_values.dup
+ bind_attrs.keys.each_with_index do |column, i|
+ real_column = db_columns_with_values[i].first
+ bind_attrs[column] = klass.connection.substitute_at(real_column, i)
+ end
+ stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(bind_attrs)
+ klass.connection.update stmt, 'SQL', db_columns_with_values
end
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index f08b9c614d..e04a3d0976 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -33,18 +33,16 @@ module ActiveRecord
# 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"}>, ...]
def find_by_sql(sql, binds = [])
- logging_query_plan do
- result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
- column_types = {}
+ result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
+ column_types = {}
- if result_set.respond_to? :column_types
- column_types = result_set.column_types
- else
- ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
- end
-
- result_set.map { |record| instantiate(record, column_types) }
+ if result_set.respond_to? :column_types
+ column_types = result_set.column_types
+ else
+ ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
end
+
+ result_set.map { |record| instantiate(record, column_types) }
end
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
@@ -57,10 +55,8 @@ module ActiveRecord
#
# 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
- sql = sanitize_conditions(sql)
- connection.select_value(sql, "#{name} Count").to_i
- end
+ sql = sanitize_conditions(sql)
+ connection.select_value(sql, "#{name} Count").to_i
end
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index aceb70bc45..64eac3aca7 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -112,7 +112,18 @@ module ActiveRecord
`config/application.rb` file and any `mass_assignment_sanitizer` options
from your `config/environments/*.rb` files.
- See http://guides.rubyonrails.org/security.html#mass-assignment for more information
+ See http://guides.rubyonrails.org/security.html#mass-assignment for more information.
+ EOF
+ end
+
+ unless app.config.active_record.delete(:auto_explain_threshold_in_seconds).nil?
+ ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, []
+ The Active Record auto explain feature has been removed.
+
+ To disable this message remove the `active_record.auto_explain_threshold_in_seconds`
+ option from the `config/environments/*.rb` config file.
+
+ See http://guides.rubyonrails.org/4_0_release_notes.html for more information.
EOF
end
@@ -124,7 +135,7 @@ module ActiveRecord
To disable this message remove the `observers` option from your
`config/application.rb` or from your initializers.
- See http://guides.rubyonrails.org/4_0_release_notes.html for more information
+ See http://guides.rubyonrails.org/4_0_release_notes.html for more information.
EOF
end
ensure
@@ -141,20 +152,11 @@ module ActiveRecord
# and then establishes the connection.
initializer "active_record.initialize_database" do |app|
ActiveSupport.on_load(:active_record) do
- unless ENV['DATABASE_URL']
- self.configurations = app.config.database_configuration
- end
+ self.configurations = app.config.database_configuration
establish_connection
end
end
- initializer "active_record.validate_explain_support" do |app|
- if app.config.active_record[:auto_explain_threshold_in_seconds] &&
- !ActiveRecord::Base.connection.supports_explain?
- warn "auto_explain_threshold_in_seconds is set but will be ignored because your adapter does not support this feature. Please unset the configuration to avoid this warning."
- end
- end
-
# Expose database runtime to controller for logging.
initializer "active_record.log_runtime" do |app|
require "active_record/railties/controller_runtime"
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 0053530f73..bc50802c4a 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -444,17 +444,7 @@ module ActiveRecord
#
# Post.where(published: true).load # => #<ActiveRecord::Relation>
def load
- unless loaded?
- # We monitor here the entire execution rather than individual SELECTs
- # because from the point of view of the user fetching the records of a
- # relation is a single unit of work. You want to know if this call takes
- # too long, not if the individual queries take too long.
- #
- # It could be the case that none of the queries involved surpass the
- # threshold, and at the same time the sum of them all does. The user
- # should get a query plan logged in that case.
- logging_query_plan { exec_queries }
- end
+ exec_queries unless loaded?
self
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index f10c290755..7f95181c67 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -13,6 +13,12 @@ module ActiveRecord
#
# Person.count(:age, distinct: true)
# # => counts the number of different age values
+ #
+ # If +count+ is used with +group+, it returns a Hash whose keys represent the aggregated column,
+ # and the values are the respective amounts:
+ #
+ # Person.group(:city).count
+ # # => { 'Rome' => 5, 'Paris' => 3 }
def count(column_name = nil, options = {})
column_name, options = nil, column_name if column_name.is_a?(Hash)
calculate(:count, column_name, options)
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 615309964c..00a506c3a7 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -12,7 +12,7 @@ module ActiveRecord
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
- :connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
+ :connection, :columns_hash, :to => :klass
module ClassSpecificRelation
extend ActiveSupport::Concern
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 7ddaea1bb0..14520381c9 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -58,7 +58,7 @@ module ActiveRecord
# order. The order will depend on the database implementation.
# If an order is supplied it will be respected.
#
- # Person.take # returns an object fetched by SELECT * FROM people
+ # Person.take # returns an object fetched by SELECT * FROM people LIMIT 1
# Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
# Person.where(["name LIKE '%?'", name]).take
def take(limit = nil)
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 537ebbef28..5cd015eba7 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -8,7 +8,7 @@ module ActiveRecord
if value.is_a?(Hash)
if value.empty?
- queries << '1 = 2'
+ queries << '1=0'
else
table = Arel::Table.new(column, default_table.engine)
association = klass.reflect_on_association(column.to_sym)
@@ -98,6 +98,11 @@ module ActiveRecord
when Class
# FIXME: I think we need to deprecate this behavior
attribute.eq(value.name)
+ when Integer, ActiveSupport::Duration
+ # Arel treats integers as literals, but they should be quoted when compared with strings
+ table = attribute.relation
+ column = table.engine.connection.schema_cache.columns_hash(table.name)[attribute.name.to_s]
+ attribute.eq(Arel::Nodes::SqlLiteral.new(table.engine.connection.quote(value, column)))
else
attribute.eq(value)
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 42849d6bc9..4b8c40592e 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -11,11 +11,11 @@ module ActiveRecord
@scope = scope
end
- # Returns a new relation expressing WHERE + NOT condition
- # according to the conditions in the arguments.
+ # 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.
+ # +not+ accepts conditions as a string, array, or hash. See #where for
+ # more details on each format.
#
# User.where.not("name = 'Jon'")
# # SELECT * FROM users WHERE NOT (name = 'Jon')
@@ -31,6 +31,10 @@ module ActiveRecord
#
# User.where.not(name: %w(Ko1 Nobu))
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
+ #
+ # User.where.not(name: "Jon", role: "admin")
+ # # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
+ #
def not(opts, *rest)
where_value = @scope.send(:build_where, opts, rest).map do |rel|
case rel
@@ -108,7 +112,8 @@ module ActiveRecord
#
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
def includes(*args)
- args.empty? ? self : spawn.includes!(*args)
+ check_if_method_has_arguments!("includes", args)
+ spawn.includes!(*args)
end
def includes!(*args) # :nodoc:
@@ -125,7 +130,8 @@ module ActiveRecord
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
# "users"."id"
def eager_load(*args)
- args.blank? ? self : spawn.eager_load!(*args)
+ check_if_method_has_arguments!("eager_load", args)
+ spawn.eager_load!(*args)
end
def eager_load!(*args) # :nodoc:
@@ -138,7 +144,8 @@ module ActiveRecord
# User.preload(:posts)
# => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
def preload(*args)
- args.blank? ? self : spawn.preload!(*args)
+ check_if_method_has_arguments!("preload", args)
+ spawn.preload!(*args)
end
def preload!(*args) # :nodoc:
@@ -155,7 +162,8 @@ module ActiveRecord
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
# # => Query now knows the string references posts, so adds a JOIN
def references(*args)
- args.blank? ? self : spawn.references!(*args)
+ check_if_method_has_arguments!("references", args)
+ spawn.references!(*args)
end
def references!(*args) # :nodoc:
@@ -189,6 +197,16 @@ module ActiveRecord
# Model.select(:field, :other_field, :and_one_more)
# # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
#
+ # You can also use one or more strings, which will be used unchanged as SELECT fields.
+ #
+ # Model.select('field AS field_one', 'other_field AS field_two')
+ # # => [#<Model field: "value", other_field: "value">]
+ #
+ # If an alias was specified, it will be accessible from the resulting objects:
+ #
+ # Model.select('field AS field_one').first.field_one
+ # # => "value"
+ #
# Accessing attributes of an object that do not have fields retrieved by a select
# will throw <tt>ActiveModel::MissingAttributeError</tt>:
#
@@ -220,8 +238,12 @@ module ActiveRecord
#
# User.group(:name)
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
+ #
+ # User.group('name AS grouped_name, age')
+ # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
def group(*args)
- args.blank? ? self : spawn.group!(*args)
+ check_if_method_has_arguments!("group", args)
+ spawn.group!(*args)
end
def group!(*args) # :nodoc:
@@ -251,7 +273,8 @@ module ActiveRecord
# User.order(:name, email: :desc)
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
def order(*args)
- args.blank? ? self : spawn.order!(*args)
+ check_if_method_has_arguments!("order", args)
+ spawn.order!(*args)
end
def order!(*args) # :nodoc:
@@ -262,6 +285,11 @@ module ActiveRecord
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
references!(references) if references.any?
+ # if a symbol is given we prepend the quoted table name
+ args = args.map { |arg|
+ arg.is_a?(Symbol) ? "#{quoted_table_name}.#{arg} ASC" : arg
+ }
+
self.order_values = args + self.order_values
self
end
@@ -276,7 +304,8 @@ module ActiveRecord
#
# generates a query with 'ORDER BY name ASC, id ASC'.
def reorder(*args)
- args.blank? ? self : spawn.reorder!(*args)
+ check_if_method_has_arguments!("reorder", args)
+ spawn.reorder!(*args)
end
def reorder!(*args) # :nodoc:
@@ -292,8 +321,14 @@ module ActiveRecord
#
# User.joins(:posts)
# => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ #
+ # You can use strings in order to customize your joins:
+ #
+ # User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
+ # => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
def joins(*args)
- args.compact.blank? ? self : spawn.joins!(*args.flatten)
+ check_if_method_has_arguments!("joins", args)
+ spawn.joins!(*args.compact.flatten)
end
def joins!(*args) # :nodoc:
@@ -439,8 +474,6 @@ module ActiveRecord
end
end
- # #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)
@@ -458,6 +491,7 @@ module ActiveRecord
# Order.having('SUM(price) > 30').group('user_id')
def having(opts, *rest)
opts.blank? ? self : spawn.having!(opts, *rest)
+ spawn.having!(opts, *rest)
end
def having!(opts, *rest) # :nodoc:
@@ -605,7 +639,6 @@ module ActiveRecord
spawn.from!(value, subquery_name)
end
- # Like #from, but modifies relation in place.
def from!(value, subquery_name = nil) # :nodoc:
self.from_value = [value, subquery_name]
self
@@ -894,5 +927,26 @@ module ActiveRecord
end
end
+ # Checks to make sure that the arguments are not blank. Note that if some
+ # blank-like object were initially passed into the query method, then this
+ # method will not raise an error.
+ #
+ # Example:
+ #
+ # Post.references() # => raises an error
+ # Post.references([]) # => does not raise an error
+ #
+ # This particular method should be called with a method_name and the args
+ # passed into that method as an input. For example:
+ #
+ # def references(*args)
+ # check_if_method_has_arguments!("references", args)
+ # ...
+ # end
+ def check_if_method_has_arguments!(method_name, args)
+ if args.blank?
+ raise ArgumentError, "The method .#{method_name}() must contain arguments."
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index cf4cf9e602..a610f479f2 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -42,21 +42,19 @@ module ActiveRecord
#
# All stored values are automatically available through accessors on the Active Record
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
- # the default accessors (using the same name as the attribute) and calling
- # <tt>read_store_attribute(store_attribute_name, attr_name)</tt> and
- # <tt>write_store_attribute(store_attribute_name, attr_name, value)</tt> to actually
- # change things.
+ # the default accessors (using the same name as the attribute) and calling <tt>super</tt>
+ # to actually change things.
#
# class Song < ActiveRecord::Base
# # Uses a stored integer to hold the volume adjustment of the song
# store :settings, accessors: [:volume_adjustment]
#
# def volume_adjustment=(decibels)
- # write_store_attribute(:settings, :volume_adjustment, decibels.to_i)
+ # super(decibels.to_i)
# end
#
# def volume_adjustment
- # read_store_attribute(:settings, :volume_adjustment).to_i
+ # super.to_i
# end
# end
module Store
@@ -75,19 +73,30 @@ module ActiveRecord
def store_accessor(store_attribute, *keys)
keys = keys.flatten
- keys.each do |key|
- define_method("#{key}=") do |value|
- write_store_attribute(store_attribute, key, value)
- end
- define_method(key) do
- read_store_attribute(store_attribute, key)
+ _store_accessors_module.module_eval do
+ keys.each do |key|
+ define_method("#{key}=") do |value|
+ write_store_attribute(store_attribute, key, value)
+ end
+
+ define_method(key) do
+ read_store_attribute(store_attribute, key)
+ end
end
end
self.stored_attributes[store_attribute] ||= []
self.stored_attributes[store_attribute] |= keys
end
+
+ def _store_accessors_module
+ @_store_accessors_module ||= begin
+ mod = Module.new
+ include mod
+ mod
+ end
+ end
end
protected
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 4a608e4f7b..33718ef0e9 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -218,9 +218,8 @@ module ActiveRecord
# after_commit :do_bar, on: :update
# after_commit :do_baz, on: :destroy
#
- # Also, to have the callback fired on create and update, but not on destroy:
- #
- # after_commit :do_zoo, if: :persisted?
+ # after_commit :do_foo_bar, :on [:create, :update]
+ # after_commit :do_bar_baz, :on [:update, :destroy]
#
# Note that transactional fixtures do not play well with this feature. Please
# use the +test_after_commit+ gem to have these hooks fired in tests.
@@ -244,12 +243,14 @@ module ActiveRecord
if options.is_a?(Hash) && options[:on]
assert_valid_transaction_action(options[:on])
options[:if] = Array(options[:if])
- options[:if] << "transaction_include_action?(:#{options[:on]})"
+ fire_on = Array(options[:on]).map(&:to_sym)
+ options[:if] << "transaction_include_any_action?(#{fire_on})"
end
end
- def assert_valid_transaction_action(action)
- unless ACTIONS.include?(action.to_sym)
+ def assert_valid_transaction_action(actions)
+ actions = Array(actions)
+ if (actions - ACTIONS).any?
raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS.join(",")}"
end
end
@@ -286,8 +287,11 @@ module ActiveRecord
end
# Call the after_commit callbacks
+ #
+ # Ensure that it is not called if the object was never persisted (failed create),
+ # but call it after the commit of a destroyed object
def committed! #:nodoc:
- run_callbacks :commit
+ run_callbacks :commit if destroyed? || persisted?
ensure
clear_transaction_record_state
end
@@ -375,14 +379,16 @@ module ActiveRecord
end
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
- def transaction_include_action?(action) #:nodoc:
- case action
- when :create
- transaction_record_state(:new_record)
- when :destroy
- destroyed?
- when :update
- !(transaction_record_state(:new_record) || destroyed?)
+ def transaction_include_any_action?(actions) #:nodoc:
+ actions.any? do |action|
+ case action
+ when :create
+ transaction_record_state(:new_record)
+ when :destroy
+ destroyed?
+ when :update
+ !(transaction_record_state(:new_record) || destroyed?)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index 0c35adc11d..c0471bb506 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -3,7 +3,7 @@ module ActiveRecord
MAJOR = 4
MINOR = 0
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index f9149c1819..0af7cbf74f 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "models/book"
module ActiveRecord
class AdapterTest < ActiveRecord::TestCase
@@ -6,6 +7,19 @@ module ActiveRecord
@connection = ActiveRecord::Base.connection
end
+ ##
+ # PostgreSQL does not support null bytes in strings
+ unless current_adapter?(:PostgreSQLAdapter)
+ def test_update_prepared_statement
+ b = Book.create(name: "my \x00 book")
+ b.reload
+ assert_equal "my \x00 book", b.name
+ b.update_attributes(name: "my other \x00 book")
+ b.reload
+ assert_equal "my other \x00 book", b.name
+ end
+ end
+
def test_tables
tables = @connection.tables
assert tables.include?("accounts")
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index b67d70ede7..b965983fec 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -19,6 +19,9 @@ class MysqlConnectionTest < ActiveRecord::TestCase
def test_connect_with_url
run_without_connection do |orig|
ar_config = ARTest.connection_config['arunit']
+
+ skip "This test doesn't work with custom socket location" if ar_config['socket']
+
url = "mysql://#{ar_config["username"]}@localhost/#{ar_config["database"]}"
Klass.establish_connection(url)
assert_equal ar_config['database'], Klass.connection.current_database
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 7351ed9013..33c796191e 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -545,13 +545,13 @@ _SQL
def test_update_bit_string
new_bit_string = '11111111'
- new_bit_string_varying = 'FF'
+ new_bit_string_varying = '11111110'
assert @first_bit_string.bit_string = new_bit_string
assert @first_bit_string.bit_string_varying = new_bit_string_varying
assert @first_bit_string.save
assert @first_bit_string.reload
- assert_equal @first_bit_string.bit_string, new_bit_string
- assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying
+ assert_equal new_bit_string, @first_bit_string.bit_string
+ assert_equal new_bit_string_varying, @first_bit_string.bit_string_varying
end
def test_update_oid
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index be2d30641e..1de7ee0846 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -73,6 +73,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
+ def test_has_many_through_with_order
+ authors = Author.includes(:favorite_authors).to_a
+ assert_no_queries { authors.map(&:favorite_authors) }
+ end
+
def test_with_two_tables_in_from_without_getting_double_quoted
posts = Post.select("posts.*").from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").order("posts.id").to_a
assert_equal 2, posts.first.comments.size
@@ -213,7 +218,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
post = posts(:welcome)
post.update!(author: nil)
- post = assert_queries(1) { Post.all.merge!(includes: {author_with_address: :author_address}).find(post.id) }
+ post = assert_queries(1) { Post.all.merge!(includes: {author_with_address: :author_address}).find(post.id) }
# find the post, then find the author which is null so no query for the author or address
assert_no_queries do
assert_equal nil, post.author_with_address
@@ -1162,4 +1167,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.where('1 = 0').scoping { Comment.preload(:post).find(1).post }
)
end
+
+ test "preloading does not cache has many association subset when preloaded with a through association" do
+ author = Author.includes(:comments_with_order_and_conditions, :posts).first
+ assert_no_queries { assert_equal 2, author.comments_with_order_and_conditions.size }
+ assert_no_queries { assert_equal 5, author.posts.size, "should not cache a subset of the association" }
+ end
+
+ test "works in combination with order(:symbol)" do
+ author = Author.includes(:posts).references(:posts).order(:name).where('posts.title IS NOT NULL').first
+ assert_equal authors(:bob), author
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index fd6d531645..1ddd380f23 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -20,6 +20,8 @@ require 'models/car'
require 'models/bulb'
require 'models/engine'
require 'models/categorization'
+require 'models/minivan'
+require 'models/speedometer'
class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase
class Invoice < ActiveRecord::Base
@@ -1747,4 +1749,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
david.posts_with_special_categorizations = []
assert_equal [], david.posts_with_special_categorizations
end
+
+ test "does not duplicate associations when used with natural primary keys" do
+ speedometer = Speedometer.create!(id: '4')
+ speedometer.minivans.create!(minivan_id: 'a-van-red' ,name: 'a van', color: 'red')
+
+ assert_equal 1, speedometer.minivans.to_a.size, "Only one association should be present:\n#{speedometer.minivans.to_a}"
+ assert_equal 1, speedometer.reload.minivans.to_a.size
+ end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 4f46459ab3..af1845c937 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -319,27 +319,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(true, cb.frickinawesome)
end
- def test_first_or_create
- parrot = Bird.first_or_create(:color => 'green', :name => 'parrot')
- assert parrot.persisted?
- the_same_parrot = Bird.first_or_create(:color => 'yellow', :name => 'macaw')
- assert_equal parrot, the_same_parrot
- end
-
- def test_first_or_create_bang
- assert_raises(ActiveRecord::RecordInvalid) { Bird.first_or_create! }
- parrot = Bird.first_or_create!(:color => 'green', :name => 'parrot')
- assert parrot.persisted?
- the_same_parrot = Bird.first_or_create!(:color => 'yellow', :name => 'macaw')
- assert_equal parrot, the_same_parrot
- end
-
- def test_first_or_initialize
- parrot = Bird.first_or_initialize(:color => 'green', :name => 'parrot')
- assert_kind_of Bird, parrot
- assert !parrot.persisted?
- assert parrot.new_record?
- assert parrot.valid?
+ def test_create_after_initialize_with_array_param
+ cbs = CustomBulb.create([{ name: 'Dude' }, { name: 'Bob' }])
+ assert_equal 'Dude', cbs[0].name
+ assert_equal 'Bob', cbs[1].name
+ assert cbs[0].frickinawesome
+ assert !cbs[1].frickinawesome
end
def test_load
@@ -1462,6 +1447,13 @@ class BasicsTest < ActiveRecord::TestCase
assert_match(/\/#{dev.id}$/, dev.cache_key)
end
+ def test_touch_should_raise_error_on_a_new_object
+ company = Company.new(:rating => 1, :name => "37signals", :firm_name => "37signals")
+ assert_raises(ActiveRecord::ActiveRecordError) do
+ company.touch :updated_at
+ end
+ end
+
def test_cache_key_format_is_precise_enough
dev = Developer.first
key = dev.cache_key
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index f0a2cdca1a..c8dfc3244b 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -20,48 +20,27 @@ module ActiveRecord
def test_url_host_no_db
spec = resolve 'abstract://foo?encoding=utf8'
assert_equal({
- :adapter => "abstract",
- :host => "foo",
- :encoding => "utf8" }, spec)
+ adapter: "abstract",
+ host: "foo",
+ encoding: "utf8" }, spec)
end
def test_url_host_db
spec = resolve 'abstract://foo/bar?encoding=utf8'
assert_equal({
- :adapter => "abstract",
- :database => "bar",
- :host => "foo",
- :encoding => "utf8" }, spec)
+ adapter: "abstract",
+ database: "bar",
+ host: "foo",
+ encoding: "utf8" }, spec)
end
def test_url_port
spec = resolve 'abstract://foo:123?encoding=utf8'
assert_equal({
- :adapter => "abstract",
- :port => 123,
- :host => "foo",
- :encoding => "utf8" }, spec)
- end
-
- def test_url_query_numeric
- spec = resolve 'abstract://foo:123?encoding=utf8&int=500&float=10.9'
- assert_equal({
- :adapter => "abstract",
- :port => 123,
- :int => 500,
- :float => 10.9,
- :host => "foo",
- :encoding => "utf8" }, spec)
- end
-
- def test_url_query_boolean
- spec = resolve 'abstract://foo:123?true=true&false=false'
- assert_equal({
- :adapter => "abstract",
- :port => 123,
- :true => true,
- :false => false,
- :host => "foo" }, spec)
+ adapter: "abstract",
+ port: 123,
+ host: "foo",
+ encoding: "utf8" }, spec)
end
def test_encoded_password
@@ -70,6 +49,14 @@ module ActiveRecord
spec = resolve "abstract://foo:#{encoded_password}@localhost/bar"
assert_equal password, spec[:password]
end
+
+ def test_descriptive_error_message_when_adapter_is_missing
+ error = assert_raise(LoadError) do
+ resolve(adapter: 'non-existing')
+ end
+
+ assert_match "Could not load 'active_record/connection_adapters/non-existing_adapter'", error.message
+ end
end
end
end
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index eca500f7e4..fe105b9d22 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -123,5 +123,14 @@ module ActiveRecord
assert duped.valid?
end
end
+
+ def test_dup_with_default_scope
+ prev_default_scopes = Topic.default_scopes
+ Topic.default_scopes = [Topic.where(:approved => true)]
+ topic = Topic.new(:approved => false)
+ assert !topic.dup.approved?, "should not be overriden by default scopes"
+ ensure
+ Topic.default_scopes = prev_default_scopes
+ end
end
end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index aa2a6d7509..b1d276f9eb 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -14,46 +14,9 @@ if ActiveRecord::Base.connection.supports_explain?
base.connection
end
- def test_logging_query_plan_with_logger
- base.logger.expects(:warn).with do |message|
- message.starts_with?('EXPLAIN for:')
- end
-
- with_threshold(0) do
- Car.where(:name => 'honda').to_a
- end
- end
-
- def test_logging_query_plan_without_logger
- original = base.logger
- base.logger = nil
-
- class << base.logger
- def warn; raise "Should not be called" end
- end
-
- with_threshold(0) do
- car = Car.where(:name => 'honda').first
- assert_equal 'honda', car.name
- end
- ensure
- base.logger = original
- end
-
- def test_collect_queries_for_explain
- base.auto_explain_threshold_in_seconds = nil
- queries = Thread.current[:available_queries_for_explain] = []
-
- with_threshold(0) do
- Car.where(:name => 'honda').to_a
- end
-
- sql, binds = queries[0]
- assert_match "SELECT", sql
- assert_match "honda", sql
- assert_equal [], binds
- ensure
- Thread.current[:available_queries_for_explain] = nil
+ def test_relation_explain
+ message = Car.where(:name => 'honda').explain
+ assert_match(/^EXPLAIN for:/, message)
end
def test_collecting_queries_for_explain
@@ -68,16 +31,6 @@ if ActiveRecord::Base.connection.supports_explain?
assert_equal [cars(:honda)], result
end
- def test_logging_query_plan_when_counting_by_sql
- base.logger.expects(:warn).with do |message|
- message.starts_with?('EXPLAIN for:')
- end
-
- with_threshold(0) do
- Car.count_by_sql "SELECT COUNT(*) FROM cars WHERE name = 'honda'"
- end
- end
-
def test_exec_explain_with_no_binds
sqls = %w(foo bar)
binds = [[], []]
@@ -113,25 +66,8 @@ if ActiveRecord::Base.connection.supports_explain?
base.logger.expects(:warn).never
- with_threshold(0) do
- Car.where(:name => 'honda').to_a
- end
- end
-
- def test_silence_auto_explain
- base.expects(:collecting_sqls_for_explain).never
- base.logger.expects(:warn).never
- base.silence_auto_explain do
- with_threshold(0) { Car.all.to_a }
- end
+ Car.where(:name => 'honda').to_a
end
- def with_threshold(threshold)
- current_threshold = base.auto_explain_threshold_in_seconds
- base.auto_explain_threshold_in_seconds = threshold
- yield
- ensure
- base.auto_explain_threshold_in_seconds = current_threshold
- end
end
end
diff --git a/activerecord/test/cases/migration/rename_column_test.rb b/activerecord/test/cases/migration/columns_test.rb
index 8f6918d06a..e52809f0f8 100644
--- a/activerecord/test/cases/migration/rename_column_test.rb
+++ b/activerecord/test/cases/migration/columns_test.rb
@@ -2,7 +2,7 @@ require "cases/migration/helper"
module ActiveRecord
class Migration
- class RenameColumnTest < ActiveRecord::TestCase
+ class ColumnsTest < ActiveRecord::TestCase
include ActiveRecord::Migration::TestHelper
self.use_transactional_fixtures = false
@@ -86,8 +86,37 @@ module ActiveRecord
assert_equal 1, connection.indexes('test_models').size
rename_column "test_models", "hat_name", "name"
- # FIXME: should we rename the index if it's name was autogenerated by rails?
- assert_equal ['index_test_models_on_hat_name'], connection.indexes('test_models').map(&:name)
+
+ assert_equal ['index_test_models_on_name'], connection.indexes('test_models').map(&:name)
+ end
+
+ def test_rename_column_with_multi_column_index
+ add_column "test_models", :hat_size, :integer
+ add_column "test_models", :hat_style, :string, limit: 100
+ add_index "test_models", ["hat_style", "hat_size"], unique: true
+
+ rename_column "test_models", "hat_size", 'size'
+ if current_adapter? :OracleAdapter
+ assert_equal ['i_test_models_hat_style_size'], connection.indexes('test_models').map(&:name)
+ else
+ assert_equal ['index_test_models_on_hat_style_and_size'], connection.indexes('test_models').map(&:name)
+ end
+
+ rename_column "test_models", "hat_style", 'style'
+ if current_adapter? :OracleAdapter
+ assert_equal ['i_test_models_style_size'], connection.indexes('test_models').map(&:name)
+ else
+ assert_equal ['index_test_models_on_style_and_size'], connection.indexes('test_models').map(&:name)
+ end
+ end
+
+ def test_rename_column_does_not_rename_custom_named_index
+ add_column "test_models", :hat_name, :string
+ add_index :test_models, :hat_name, :name => 'idx_hat_name'
+
+ assert_equal 1, connection.indexes('test_models').size
+ rename_column "test_models", "hat_name", "name"
+ assert_equal ['idx_hat_name'], connection.indexes('test_models').map(&:name)
end
def test_remove_column_with_index
@@ -107,7 +136,7 @@ module ActiveRecord
assert_equal 1, connection.indexes('test_models').size
remove_column("test_models", "hat_size")
- # Every database and/or database adapter has their own behavior
+ # Every database and/or database adapter has their own behavior
# if it drops the multi-column index when any of the indexed columns dropped by remove_column.
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
assert_equal [], connection.indexes('test_models').map(&:name)
@@ -197,6 +226,17 @@ module ActiveRecord
assert_equal ['test_models_categories_idx'], connection.indexes('test_models').map(&:name)
end
+ def test_change_column_with_long_index_name
+ table_name_prefix = 'test_models_'
+ long_index_name = table_name_prefix + ('x' * (connection.allowed_index_name_length - table_name_prefix.length))
+ add_column "test_models", "category", :string
+ add_index :test_models, :category, name: long_index_name
+
+ change_column "test_models", "category", :string, null: false, default: 'article'
+
+ assert_equal [long_index_name], connection.indexes('test_models').map(&:name)
+ end
+
def test_change_column_default
add_column "test_models", "first_name", :string
connection.change_column_default "test_models", "first_name", "Tester"
@@ -213,6 +253,20 @@ module ActiveRecord
def test_remove_column_no_second_parameter_raises_exception
assert_raise(ArgumentError) { connection.remove_column("funny") }
end
+
+ def test_removing_and_renaming_column_preserves_custom_primary_key
+ connection.create_table "my_table", primary_key: "my_table_id", force: true do |t|
+ t.integer "col_one"
+ t.string "col_two", limit: 128, null: false
+ end
+
+ remove_column("my_table", "col_two")
+ rename_column("my_table", "col_one", "col_three")
+
+ assert_equal 'my_table_id', connection.primary_key('my_table')
+ ensure
+ connection.drop_table(:my_table) rescue nil
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb
index a41f2c10f0..0e375af6e8 100644
--- a/activerecord/test/cases/migration/index_test.rb
+++ b/activerecord/test/cases/migration/index_test.rb
@@ -55,19 +55,31 @@ module ActiveRecord
assert_raise(ArgumentError) { connection.remove_index(table_name, "no_such_index") }
end
- def test_add_index_name_length_limit
- good_index_name = 'x' * connection.index_name_length
+ def test_add_index_works_with_long_index_names
+ connection.add_index(table_name, "foo", name: good_index_name)
+
+ assert connection.index_name_exists?(table_name, good_index_name, false)
+ connection.remove_index(table_name, name: good_index_name)
+ end
+
+ def test_add_index_does_not_accept_too_long_index_names
too_long_index_name = good_index_name + 'x'
- assert_raises(ArgumentError) {
- connection.add_index(table_name, "foo", :name => too_long_index_name)
+ e = assert_raises(ArgumentError) {
+ connection.add_index(table_name, "foo", name: too_long_index_name)
}
+ assert_match(/too long; the limit is #{connection.allowed_index_name_length} characters/, e.message)
assert_not connection.index_name_exists?(table_name, too_long_index_name, false)
connection.add_index(table_name, "foo", :name => good_index_name)
+ end
+
+ def test_internal_index_with_name_matching_database_limit
+ good_index_name = 'x' * connection.index_name_length
+ connection.add_index(table_name, "foo", name: good_index_name, internal: true)
assert connection.index_name_exists?(table_name, good_index_name, false)
- connection.remove_index(table_name, :name => good_index_name)
+ connection.remove_index(table_name, name: good_index_name)
end
def test_index_symbol_names
@@ -196,6 +208,12 @@ module ActiveRecord
connection.remove_index("testings", "last_name")
assert !connection.index_exists?("testings", "last_name")
end
+
+ private
+ def good_index_name
+ 'x' * connection.allowed_index_name_length
+ end
+
end
end
end
diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb
index 21901bec3c..22dbd7c38b 100644
--- a/activerecord/test/cases/migration/rename_table_test.rb
+++ b/activerecord/test/cases/migration/rename_table_test.rb
@@ -63,7 +63,17 @@ module ActiveRecord
connection.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
- assert connection.indexes(:octopi).first.columns.include?("url")
+ index = connection.indexes(:octopi).first
+ assert index.columns.include?("url")
+ assert_equal 'index_octopi_on_url', index.name
+ end
+
+ def test_rename_table_does_not_rename_custom_named_index
+ add_index :test_models, :url, name: 'special_url_idx'
+
+ rename_table :test_models, :octopi
+
+ assert_equal ['special_url_idx'], connection.indexes(:octopi).map(&:name)
end
def test_rename_table_for_postgresql_should_also_rename_default_sequence
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index b936cca875..8156f99037 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -399,6 +399,14 @@ class PersistencesTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') }
end
+ def test_string_ids
+ # FIXME: Fix this failing test
+ skip "Failing test. We need this fixed before 4.0.0"
+ mv = Minivan.where(:minivan_id => 1234).first_or_initialize
+ assert mv.new_record?
+ assert_equal '1234', mv.minivan_id
+ end
+
def test_update_attribute_with_one_updated
t = Topic.first
t.update_attribute(:title, 'super_title')
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 3dd11ae89d..0ad05223d4 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -122,35 +122,35 @@ module ActiveRecord
def test_quote_float
float = 1.2
assert_equal float.to_s, @quoter.quote(float, nil)
- assert_equal float.to_s, @quoter.quote(float, Object.new)
+ assert_equal float.to_s, @quoter.quote(float, FakeColumn.new(:float))
end
def test_quote_fixnum
fixnum = 1
assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
- assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new)
+ assert_equal fixnum.to_s, @quoter.quote(fixnum, FakeColumn.new(:integer))
end
def test_quote_bignum
bignum = 1 << 100
assert_equal bignum.to_s, @quoter.quote(bignum, nil)
- assert_equal bignum.to_s, @quoter.quote(bignum, Object.new)
+ assert_equal bignum.to_s, @quoter.quote(bignum, FakeColumn.new(:integer))
end
def test_quote_bigdecimal
bigdec = BigDecimal.new((1 << 100).to_s)
assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, nil)
- assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new)
+ assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, FakeColumn.new(:decimal))
end
def test_dates_and_times
@quoter.extend(Module.new { def quoted_date(value) 'lol' end })
assert_equal "'lol'", @quoter.quote(Date.today, nil)
- assert_equal "'lol'", @quoter.quote(Date.today, Object.new)
+ assert_equal "'lol'", @quoter.quote(Date.today, FakeColumn.new(:date))
assert_equal "'lol'", @quoter.quote(Time.now, nil)
- assert_equal "'lol'", @quoter.quote(Time.now, Object.new)
+ assert_equal "'lol'", @quoter.quote(Time.now, FakeColumn.new(:time))
assert_equal "'lol'", @quoter.quote(DateTime.now, nil)
- assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new)
+ assert_equal "'lol'", @quoter.quote(DateTime.now, FakeColumn.new(:datetime))
end
def test_crazy_object
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index c43c7601a2..53cdf89b1f 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -108,5 +108,30 @@ module ActiveRecord
assert_equal 4, Edge.where(blank).order("sink_id").to_a.size
end
end
+
+ def test_where_with_integer_for_string_column
+ count = Post.where(:title => 0).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_float_for_string_column
+ count = Post.where(:title => 0.0).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_boolean_for_string_column
+ count = Post.where(:title => false).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_decimal_for_string_column
+ count = Post.where(:title => BigDecimal.new(0)).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_duration_for_string_column
+ count = Post.where(:title => 0.seconds).count
+ assert_equal 0, count
+ end
end
end
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index 7388324a0d..8e6c38706f 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -391,19 +391,19 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scope_with_inheritance
wheres = InheritedPoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal 50000, wheres[:salary]
+ assert_equal Arel.sql("50000"), wheres[:salary]
end
def test_default_scope_with_module_includes
wheres = ModuleIncludedPoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal 50000, wheres[:salary]
+ assert_equal Arel.sql("50000"), wheres[:salary]
end
def test_default_scope_with_multiple_calls
wheres = MultiplePoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal 50000, wheres[:salary]
+ assert_equal Arel.sql("50000"), wheres[:salary]
end
def test_scope_overwrites_default
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 92dc575d37..fd0b05cb77 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -180,19 +180,32 @@ module ActiveRecord
class RelationMutationTest < ActiveSupport::TestCase
class FakeKlass < Struct.new(:table_name, :name)
+ def quoted_table_name
+ %{"#{table_name}"}
+ end
end
def relation
- @relation ||= Relation.new FakeKlass, :b
+ @relation ||= Relation.new FakeKlass.new('posts'), :b
end
- (Relation::MULTI_VALUE_METHODS - [:references, :extending]).each do |method|
+ (Relation::MULTI_VALUE_METHODS - [:references, :extending, :order]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal [:foo], relation.public_send("#{method}_values")
end
end
+ test "#order!" do
+ assert relation.order!('name ASC').equal?(relation)
+ assert_equal ['name ASC'], relation.order_values
+ end
+
+ test "#order! with symbol prepends the table name" do
+ assert relation.order!(:name).equal?(relation)
+ assert_equal ['"posts".name ASC'], relation.order_values
+ end
+
test '#references!' do
assert relation.references!(:foo).equal?(relation)
assert relation.references_values.include?('foo')
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 379c0c0758..8298d7534c 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -321,6 +321,22 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 1, person_with_reader_and_post.size
end
+ def test_no_arguments_to_query_methods_raise_errors
+ assert_raises(ArgumentError) { Topic.references() }
+ assert_raises(ArgumentError) { Topic.includes() }
+ assert_raises(ArgumentError) { Topic.preload() }
+ assert_raises(ArgumentError) { Topic.group() }
+ assert_raises(ArgumentError) { Topic.reorder() }
+ end
+
+ def test_blank_like_arguments_to_query_methods_dont_raise_errors
+ assert_nothing_raised { Topic.references([]) }
+ assert_nothing_raised { Topic.includes([]) }
+ assert_nothing_raised { Topic.preload([]) }
+ assert_nothing_raised { Topic.group([]) }
+ assert_nothing_raised { Topic.reorder([]) }
+ end
+
def test_scoped_responds_to_delegated_methods
relation = Topic.all
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index bfecc0d1e9..1147b9a09e 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -261,22 +261,22 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_includes_inet_shorthand_definition
output = standard_dump
- if %r{create_table "postgresql_network_address"} =~ output
- assert_match %r{t.inet "inet_address"}, output
+ if %r{create_table "postgresql_network_addresses"} =~ output
+ assert_match %r{t.inet\s+"inet_address",\s+default: "192.168.1.1"}, output
end
end
def test_schema_dump_includes_cidr_shorthand_definition
output = standard_dump
- if %r{create_table "postgresql_network_address"} =~ output
- assert_match %r{t.cidr "cidr_address"}, output
+ if %r{create_table "postgresql_network_addresses"} =~ output
+ assert_match %r{t.cidr\s+"cidr_address",\s+default: "192.168.1.0/24"}, output
end
end
def test_schema_dump_includes_macaddr_shorthand_definition
output = standard_dump
- if %r{create_table "postgresql_network_address"} =~ output
- assert_match %r{t.macaddr "macaddr_address"}, output
+ if %r{create_table "postgresql_network_addresses"} =~ output
+ assert_match %r{t.macaddr\s+"mac_address",\s+default: "ff:ff:ff:ff:ff:ff"}, output
end
end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 43bf285ba9..3e32d866ee 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -35,6 +35,12 @@ class StoreTest < ActiveRecord::TestCase
assert_equal '(123) 456-7890', @john.phone_number
end
+ test "overriding a read accessor using super" do
+ @john.settings[:color] = nil
+
+ assert_equal 'red', @john.color
+ end
+
test "updating the store will mark it as changed" do
@john.color = 'red'
assert @john.settings_changed?
@@ -66,6 +72,12 @@ class StoreTest < ActiveRecord::TestCase
assert_equal '1234567890', @john.settings[:phone_number]
end
+ test "overriding a write accessor using super" do
+ @john.color = 'yellow'
+
+ assert_equal 'blue', @john.color
+ end
+
test "preserve store attributes data in HashWithIndifferentAccess format without any conversion" do
@john.json_data = ActiveSupport::HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy')
@john.height = 'low'
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index bb034848e1..777a2b70dd 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -113,6 +113,18 @@ class TimestampTest < ActiveRecord::TestCase
assert_not_equal previously_owner_updated_at, pet.owner.updated_at
end
+ def test_saving_a_new_record_belonging_to_invalid_parent_with_touch_should_not_raise_exception
+ klass = Class.new(Owner) do
+ def self.name; 'Owner'; end
+ validate { errors.add(:base, :invalid) }
+ end
+
+ pet = Pet.new(owner: klass.new)
+ pet.save!
+
+ assert pet.owner.new_record?
+ end
+
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
klass = Class.new(ActiveRecord::Base) do
def self.name; 'Pet'; end
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 869892e33f..eb4ffd4498 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -5,9 +5,29 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
fixtures :topics
+ class ReplyWithCallbacks < ActiveRecord::Base
+ self.table_name = :topics
+
+ belongs_to :topic, foreign_key: "parent_id"
+
+ validates_presence_of :content
+
+ after_commit :do_after_commit, on: :create
+
+ def history
+ @history ||= []
+ end
+
+ def do_after_commit
+ history << :commit_on_create
+ end
+ end
+
class TopicWithCallbacks < ActiveRecord::Base
self.table_name = :topics
+ has_many :replies, class_name: "ReplyWithCallbacks", foreign_key: "parent_id"
+
after_commit{|record| record.send(:do_after_commit, nil)}
after_commit(:on => :create){|record| record.send(:do_after_commit, :create)}
after_commit(:on => :update){|record| record.send(:do_after_commit, :update)}
@@ -93,6 +113,13 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
assert_equal [:commit_on_create], @new_record.history
end
+ def test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association
+ topic = TopicWithCallbacks.create!(:title => "New topic", :written_on => Date.today)
+ reply = topic.replies.create
+
+ assert_equal [], reply.history
+ end
+
def test_call_after_rollback_after_transaction_rollsback
@first.after_commit_block{|r| r.history << :after_commit}
@first.after_rollback_block{|r| r.history << :after_rollback}
@@ -285,3 +312,38 @@ class SaveFromAfterCommitBlockTest < ActiveRecord::TestCase
assert_equal true, topic.record_updated
end
end
+
+class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ class TopicWithCallbacksOnMultipleActions < ActiveRecord::Base
+ self.table_name = :topics
+
+ after_commit(on: [:create, :destroy]) { |record| record.history << :create_and_destroy }
+ after_commit(on: [:create, :update]) { |record| record.history << :create_and_update }
+ after_commit(on: [:update, :destroy]) { |record| record.history << :update_and_destroy }
+
+ def clear_history
+ @history = []
+ end
+
+ def history
+ @history ||= []
+ end
+ end
+
+ def test_after_commit_on_multiple_actions
+ topic = TopicWithCallbacksOnMultipleActions.new
+ topic.save
+ assert_equal [:create_and_update, :create_and_destroy], topic.history
+
+ topic.clear_history
+ topic.approved = true
+ topic.save
+ assert_equal [:update_and_destroy, :create_and_update], topic.history
+
+ topic.clear_history
+ topic.destroy
+ assert_equal [:update_and_destroy, :create_and_destroy], topic.history
+ end
+end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 546737b398..6d66342fa5 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -460,7 +460,7 @@ class TransactionTest < ActiveRecord::TestCase
assert !transaction.state.committed?
transaction.perform_rollback
-
+
assert transaction.state.rolledback?
assert !transaction.state.committed?
end
@@ -474,7 +474,7 @@ class TransactionTest < ActiveRecord::TestCase
assert !transaction.state.committed?
transaction.perform_commit
-
+
assert !transaction.state.rolledback?
assert transaction.state.committed?
end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 024fede266..4c3b71e8f9 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -27,4 +27,13 @@ class Admin::User < ActiveRecord::Base
def phone_number=(value)
write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/,''))
end
+
+ def color
+ super || 'red'
+ end
+
+ def color=(value)
+ value = 'blue' unless %w(black red green blue).include?(value)
+ super
+ end
end
diff --git a/activerecord/test/models/speedometer.rb b/activerecord/test/models/speedometer.rb
index 0a7d38d8ec..497c3aba9a 100644
--- a/activerecord/test/models/speedometer.rb
+++ b/activerecord/test/models/speedometer.rb
@@ -1,4 +1,6 @@
class Speedometer < ActiveRecord::Base
self.primary_key = :speedometer_id
belongs_to :dashboard
+
+ has_many :minivans
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 83b50030bd..d8271ac8d1 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -145,9 +145,9 @@ _SQL
execute <<_SQL
CREATE TABLE postgresql_network_addresses (
id SERIAL PRIMARY KEY,
- cidr_address CIDR,
- inet_address INET,
- mac_address MACADDR
+ cidr_address CIDR default '192.168.1.0/24',
+ inet_address INET default '192.168.1.1',
+ mac_address MACADDR default 'ff:ff:ff:ff:ff:ff'
);
_SQL
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index cd9835259a..d789b6cb7a 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -540,6 +540,8 @@ ActiveRecord::Schema.define do
create_table :price_estimates, :force => true do |t|
t.string :estimate_of_type
t.integer :estimate_of_id
+ t.string :thing_type
+ t.integer :thing_id
t.integer :price
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index fcfa5a5b81..c47cb75274 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,8 +1,60 @@
## Rails 4.0.0 (unreleased) ##
+* Fix deletion of empty directories in ActiveSupport::Cache::FileStore.
+
+ *Charles Jones*
+
+## Rails 4.0.0.beta1 (February 25, 2013) ##
+
+* Prevent `DateTime#change` from truncating the second fraction, when seconds
+ do not need to be changed.
+
+ *Chris Baynes*
+
+* Added `ActiveSupport::TimeWithZone#to_r` for `Time#at` compatibility.
+
+ Before this change:
+
+ Time.zone = 'Tokyo'
+ time = Time.zone.now
+ time == Time.at(time) # => false
+
+ After the change:
+
+ Time.zone = 'Tokyo'
+ time = Time.zone.now
+ time == Time.at(time) # => true
+
+ *stopdropandrew*
+
+* `ActiveSupport::NumberHelper#number_to_human` returns the number unaltered when
+ the given units hash does not contain the needed key, e.g. when the number provided
+ is less than the largest key provided.
+ Fixes #9269.
+
+ Examples:
+
+ number_to_human(123, units: {}) # => 123
+ number_to_human(123, units: { thousand: 'k' }) # => 123
+
+ *Michael Hoffman*
+
+* Added `beginning_of_minute` support to core ext calculations for `Time` and `DateTime`.
+
+ *Gagan Awhad*
+
+* Add `:nsec` date format.
+
+ *Jamie Gaskins*
+
+* `ActiveSupport::Gzip.compress` allows two optional arguments for compression
+ level and strategy.
+
+ *Beyond*
+
* Modify `TimeWithZone#as_json` to include 3 decimal places of sub-second accuracy
by default, which is optional as per the ISO8601 spec, but extremely useful. Also
- the default behaviour of Date#toJSON() in recent versions of Chrome, Safari and
+ the default behaviour of `Date#toJSON()` in recent versions of Chrome, Safari and
Firefox.
*James Harton*
@@ -15,7 +67,7 @@
*Andrew White*
* Extract `ActiveSupport::Testing::Performance` into https://github.com/rails/rails-perftest
- You can add the gem to your Gemfile to keep using performance tests.
+ You can add the gem to your `Gemfile` to keep using performance tests.
gem 'rails-perftest'
@@ -42,7 +94,7 @@
*Kelly Stannard*
* It's now possible to compare `Date`, `DateTime`, `Time` and `TimeWithZone`
- with `Infinity`. This allows to create date/time ranges with one infinite bound.
+ with `Float::INFINITY`. This allows to create date/time ranges with one infinite bound.
Example:
range = Range.new(Date.today, Float::INFINITY)
@@ -75,13 +127,13 @@
* Remove surrogate unicode character encoding from `ActiveSupport::JSON.encode`
The encoding scheme was broken for unicode characters outside the basic multilingual plane;
- since json is assumed to be `UTF-8`, and we already force the encoding to `UTF-8`,
+ since json is assumed to be UTF-8, and we already force the encoding to UTF-8,
simply pass through the un-encoded characters.
*Brett Carter*
* Deprecate `Time.time_with_date_fallback`, `Time.utc_time` and `Time.local_time`.
- These methods were added to handle the limited range of Ruby's native Time
+ These methods were added to handle the limited range of Ruby's native `Time`
implementation. Those limitations no longer apply so we are deprecating them in 4.0
and they will be removed in 4.1.
@@ -89,16 +141,17 @@
* Deprecate `Date#to_time_in_current_zone` and add `Date#in_time_zone`. *Andrew White*
-* Add `String#in_time_zone` method to convert a string to an ActiveSupport::TimeWithZone. *Andrew White*
+* Add `String#in_time_zone` method to convert a string to an `ActiveSupport::TimeWithZone`. *Andrew White*
* Deprecate `ActiveSupport::BasicObject` in favor of `ActiveSupport::ProxyObject`.
- This class is used for proxy classes. It avoids confusion with Ruby's BasicObject
+ 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]
+* Patched `Marshal#load` to work with constant autoloading. Fixes autoloading
+ with cache stores that rely on `Marshal` (`MemCacheStore` and `FileStore`).
+ Fixes #8167.
*Uriel Katz*
@@ -113,9 +166,9 @@
*Olek Janiszewski*
-* No longer proxy ActiveSupport::Multibyte#class. *Steve Klabnik*
+* No longer proxy `ActiveSupport::Multibyte#class`. *Steve Klabnik*
-* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead. *Carlos Antonio da Silva*
+* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from minitest instead. *Carlos Antonio da Silva*
* `XmlMini.with_backend` now may be safely used with threads:
@@ -130,18 +183,17 @@
*Nikita Afanasenko*
-* Dependencies no longer trigger Kernel#autoload in remove_constant [fixes #8213]. *Xavier Noria*
+* Dependencies no longer trigger `Kernel#autoload` in `remove_constant`. Fixes #8213. *Xavier Noria*
-* Simplify mocha integration and remove monkey-patches, bumping mocha to 0.13.0. *James Mead*
+* Simplify `mocha` integration and remove monkey-patches, bumping `mocha` to 0.13.0. *James Mead*
-* `#as_json` isolates options when encoding a hash.
- Fix #8182
+* `#as_json` isolates options when encoding a hash. Fixes #8182.
*Yves Senn*
-* Deprecate Hash#diff in favor of MiniTest's #diff. *Steve Klabnik*
+* Deprecate `Hash#diff` in favor of minitest's #diff. *Steve Klabnik*
-* Kernel#capture can catch output from subprocesses *Dmitry Vorotilin*
+* `Kernel#capture` can catch output from subprocesses. *Dmitry Vorotilin*
* `to_xml` conversions now use builder's `tag!` method instead of explicit invocation of `method_missing`.
@@ -149,27 +201,30 @@
* Fixed timezone mapping of the Solomon Islands. *Steve Klabnik*
-* Make callstack attribute optional in
- ActiveSupport::Deprecation::Reporting methods `warn` and `deprecation_warning`
+* Make callstack attribute optional in `ActiveSupport::Deprecation::Reporting`
+ methods `warn` and `deprecation_warning`.
*Alexey Gaziev*
-* Implement HashWithIndifferentAccess#replace so key? works correctly. *David Graham*
+* Implement `HashWithIndifferentAccess#replace` so `key?` works correctly. *David Graham*
+
+* Handle the possible permission denied errors `atomic.rb` might trigger due to its `chown`
+ and `chmod` calls.
-* Handle the possible Permission Denied errors atomic.rb might trigger due to its chown and chmod calls. *Daniele Sluijters*
+ *Daniele Sluijters*
-* Hash#extract! returns only those keys that present in the receiver.
+* `Hash#extract!` returns only those keys that present in the receiver.
{a: 1, b: 2}.extract!(:a, :x) # => {:a => 1}
*Mikhail Dieterle*
-* Hash#extract! returns the same subclass, that the receiver is. I.e.
- HashWithIndifferentAccess#extract! returns HashWithIndifferentAccess instance.
+* `Hash#extract!` returns the same subclass, that the receiver is. I.e.
+ `HashWithIndifferentAccess#extract!` returns a `HashWithIndifferentAccess` instance.
*Mikhail Dieterle*
-* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. *Brian Durand*
+* Optimize `ActiveSupport::Cache::Entry` to reduce memory and processing overhead. *Brian Durand*
* Tests tag the Rails log with the current test class and test case:
@@ -178,7 +233,7 @@
*Jeremy Kemper*
-* Add `logger.push_tags` and `.pop_tags` to complement logger.tagged:
+* Add `logger.push_tags` and `.pop_tags` to complement `logger.tagged`:
class Job
def before
@@ -214,6 +269,7 @@
class User
include ActiveSupport::Configurable
+
config_accessor :hair_colors do
[:brown, :black, :blonde, :red]
end
@@ -223,7 +279,7 @@
*Larry Lv*
-* ActiveSupport::Benchmarkable#silence has been deprecated due to its lack of
+* `ActiveSupport::Benchmarkable#silence` has been deprecated due to its lack of
thread safety. It will be removed without replacement in Rails 4.1.
*Steve Klabnik*
@@ -233,14 +289,14 @@
*Pranas Kiziela*
-* ActiveSupport::Deprecation is now a class. It is possible to create an instance
+* `ActiveSupport::Deprecation` is now a class. It is possible to create an instance
of deprecator. Backwards compatibility has been preserved.
You can choose which instance of the deprecator will be used.
deprecate :method_name, deprecator: deprecator_instance
- You can use ActiveSupport::Deprecation in your gem.
+ You can use `ActiveSupport::Deprecation` in your gem.
require 'active_support/deprecation'
require 'active_support/core_ext/module/deprecation'
@@ -267,7 +323,7 @@
* `ERB::Util.html_escape` encodes single quote as `#39`. Decimal form has better support in old browsers. *Kalys Osmonov*
* `ActiveSupport::Callbacks`: deprecate monkey patch of object callbacks.
- Using the #filter method like this:
+ Using the `filter` method like this:
before_filter MyFilter.new
@@ -300,7 +356,7 @@
*Akira Matsuda*
-* Replace deprecated `memcache-client` gem with `dalli` in ActiveSupport::Cache::MemCacheStore
+* Replace deprecated `memcache-client` gem with `dalli` in `ActiveSupport::Cache::MemCacheStore`.
*Guillermo Iguaran*
@@ -314,14 +370,14 @@
*Erich Menge*
-* Add String#indent. *fxn & Ace Suares*
+* Add `String#indent`. *fxn & Ace Suares*
* Inflections can now be defined per locale. `singularize` and `pluralize`
accept locale as an extra argument.
*David Celis*
-* `Object#try` will now return nil instead of raise a NoMethodError if the
+* `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!`.
@@ -349,62 +405,64 @@
*Francesco Rodriguez*
-* 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. *Andrew Mutz*
+* `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`.
+
+ *Andrew Mutz*
* Add `Hash#transform_keys`, `Hash#transform_keys!`, `Hash#deep_transform_keys`, and `Hash#deep_transform_keys!`. *Mark McSpadden*
-* Changed xml type `datetime` to `dateTime` (with upper case letter `T`). *Angelo Capilleri*
+* Changed XML type `datetime` to `dateTime` (with upper case letter `T`). *Angelo Capilleri*
* Add `:instance_accessor` option for `class_attribute`. *Alexey Vakhov*
* `constantize` now looks in the ancestor chain. *Marc-Andre Lafortune & Andrew White*
-* Adds `Hash#deep_stringify_keys` and `Hash#deep_stringify_keys!` to convert all keys from a +Hash+ instance into strings *Lucas Húngaro*
+* Adds `Hash#deep_stringify_keys` and `Hash#deep_stringify_keys!` to convert all keys from a `Hash` instance into strings. *Lucas Húngaro*
-* Adds `Hash#deep_symbolize_keys` and `Hash#deep_symbolize_keys!` to convert all keys from a +Hash+ instance into symbols *Lucas Húngaro*
+* Adds `Hash#deep_symbolize_keys` and `Hash#deep_symbolize_keys!` to convert all keys from a `Hash` instance into symbols. *Lucas Húngaro*
* `Object#try` can't call private methods. *Vasiliy Ermolovich*
* `AS::Callbacks#run_callbacks` remove `key` argument. *Francesco Rodriguez*
-* `deep_dup` works more expectedly now and duplicates also values in +Hash+ instances and elements in +Array+ instances. *Alexey Gaziev*
+* `deep_dup` works more expectedly now and duplicates also values in `Hash` instances and elements in `Array` instances. *Alexey Gaziev*
-* Inflector no longer applies ice -> ouse to words like slice, police, ets *Wes Morgan*
+* Inflector no longer applies ice -> ouse to words like "slice", "police", etc. *Wes Morgan*
-* Add `ActiveSupport::Deprecations.behavior = :silence` to completely ignore Rails runtime deprecations *twinturbo*
+* Add `ActiveSupport::Deprecations.behavior = :silence` to completely ignore Rails runtime deprecations. *twinturbo*
-* Make Module#delegate stop using `send` - can no longer delegate to private methods. *dasch*
+* Make `Module#delegate` stop using `send` - can no longer delegate to private methods. *dasch*
-* AS::Callbacks: deprecate `:rescuable` option. *Bogdan Gusiev*
+* `ActiveSupport::Callbacks`: deprecate `:rescuable` option. *Bogdan Gusiev*
-* Adds Integer#ordinal to get the ordinal suffix string of an integer. *Tim Gildea*
+* Adds `Integer#ordinal` to get the ordinal suffix string of an integer. *Tim Gildea*
-* AS::Callbacks: `:per_key` option is no longer supported
+* `ActiveSupport::Callbacks`: `:per_key` option is no longer supported. *Bogdan Gusiev*
-* `AS::Callbacks#define_callbacks`: add `:skip_after_callbacks_if_terminated` option.
+* `ActiveSupport::Callbacks#define_callbacks`: add `:skip_after_callbacks_if_terminated` option. *Bogdan Gusiev*
-* Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it. *Carlos Antonio da Silva*
+* Add `html_escape_once` to `ERB::Util`, and delegate the `escape_once` tag helper to it. *Carlos Antonio da Silva*
-* Deprecates the compatibility method Module#local_constant_names,
- use Module#local_constants instead (which returns symbols). *fxn*
+* Deprecates the compatibility method `Module#local_constant_names`,
+ use `Module#local_constants` instead (which returns symbols). *Xavier Noria*
-* Deletes the compatibility method Module#method_names,
- use Module#methods from now on (which returns symbols). *fxn*
+* Deletes the compatibility method `Module#method_names`,
+ use `Module#methods` from now on (which returns symbols). *Xavier Noria*
-* Deletes the compatibility method Module#instance_method_names,
- use Module#instance_methods from now on (which returns symbols). *fxn*
+* Deletes the compatibility method `Module#instance_method_names`,
+ use `Module#instance_methods` from now on (which returns symbols). *Xavier Noria*
-* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger
- from Ruby stdlib.
+* `BufferedLogger` is deprecated. Use `ActiveSupport::Logger`, or the logger
+ from the Ruby standard library.
-* Unicode database updated to 6.1.0.
+ *Aaron Patterson*
-* Adds `encode_big_decimal_as_string` option to force JSON serialization of BigDecimals as numeric instead
- of wrapping them in strings for safety.
+* Unicode database updated to 6.1.0. *Norman Clarke*
-* Remove deprecated ActiveSupport::JSON::Variable. *Erich Menge*
+* Adds `encode_big_decimal_as_string` option to force JSON serialization of `BigDecimal` as numeric instead
+ of wrapping them in strings for safety.
* Optimize log subscribers to check log level before doing any processing. *Brian Durand*
diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc
index ed688ecc59..f3582767c0 100644
--- a/activesupport/README.rdoc
+++ b/activesupport/README.rdoc
@@ -12,7 +12,7 @@ The latest version of Active Support can be installed with RubyGems:
% [sudo] gem install activesupport
-Source code can be downloaded as part of the Rails project on GitHub
+Source code can be downloaded as part of the Rails project on GitHub:
* https://github.com/rails/rails/tree/master/activesupport
@@ -26,7 +26,7 @@ Active Support is released under the MIT license:
== Support
-API documentation is at
+API documentation is at:
* http://api.rubyonrails.org
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 4c9e59dbd2..c71525309b 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -20,9 +20,9 @@ Gem::Specification.new do |s|
s.rdoc_options.concat ['--encoding', 'UTF-8']
- s.add_dependency 'i18n', '~> 0.6'
+ s.add_dependency 'i18n', '~> 0.6', '>= 0.6.2'
s.add_dependency 'multi_json', '~> 1.3'
s.add_dependency 'tzinfo', '~> 0.3.33'
- s.add_dependency 'minitest', '~> 4.1'
+ s.add_dependency 'minitest', '~> 4.2'
s.add_dependency 'thread_safe','~> 0.1'
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 8e265ad863..0c55aa8a32 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -150,9 +150,9 @@ module ActiveSupport
# Delete empty directories in the cache.
def delete_empty_directories(dir)
- return if dir == cache_path
+ return if File.realpath(dir) == File.realpath(cache_path)
if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
- File.delete(dir) rescue nil
+ Dir.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 9a2dc6e7c5..3c4bfc5f1e 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -29,9 +29,9 @@ class Class
#
# class Foo; end
# class Bar < Foo; end
- # class Baz < Foo; end
+ # class Baz < Bar; end
#
- # Foo.subclasses # => [Baz, Bar]
+ # Foo.subclasses # => [Bar]
def subclasses
subclasses, chain = [], descendants
chain.each do |k|
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index 1d3682eaf2..9f0864d9bb 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -59,7 +59,7 @@ class DateTime
options.fetch(:day, day),
options.fetch(:hour, hour),
options.fetch(:min, options[:hour] ? 0 : min),
- options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec),
+ options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec + sec_fraction),
options.fetch(:offset, offset),
options.fetch(:start, start)
)
@@ -124,6 +124,18 @@ class DateTime
end
alias :at_end_of_hour :end_of_hour
+ # Returns a new DateTime representing the start of the minute (hh:mm:00).
+ def beginning_of_minute
+ change(:sec => 0)
+ end
+ alias :at_beginning_of_minute :beginning_of_minute
+
+ # Returns a new DateTime representing the end of the minute (hh:mm:59).
+ def end_of_minute
+ change(:sec => 59)
+ end
+ alias :at_end_of_minute :end_of_minute
+
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0.
#
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 1f95f62229..a3ce7dbe3f 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -188,6 +188,21 @@ class Time
end
alias :at_end_of_hour :end_of_hour
+ # Returns a new Time representing the start of the minute (x:xx:00)
+ def beginning_of_minute
+ change(:sec => 0)
+ end
+ alias :at_beginning_of_minute :beginning_of_minute
+
+ # Returns a new Time representing the end of the minute, x:xx:59.999999 (.999999999 in ruby1.9)
+ def end_of_minute
+ change(
+ :sec => 59,
+ :usec => Rational(999999999, 1000)
+ )
+ end
+ alias :at_end_of_minute :end_of_minute
+
# Returns a Range representing the whole day of the current time.
def all_day
beginning_of_day..end_of_day
diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb
index 6ef33ab683..b837c879bb 100644
--- a/activesupport/lib/active_support/gzip.rb
+++ b/activesupport/lib/active_support/gzip.rb
@@ -25,9 +25,9 @@ module ActiveSupport
end
# Compresses a string using gzip.
- def self.compress(source)
+ def self.compress(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
output = Stream.new
- gz = Zlib::GzipWriter.new(output)
+ gz = Zlib::GzipWriter.new(output, level, strategy)
gz.write(source)
gz.close
output.string
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index 188653bd9b..22521a8e93 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -1,4 +1,7 @@
begin
+ require 'active_support/core_ext/hash/deep_merge'
+ require 'active_support/core_ext/hash/except'
+ require 'active_support/core_ext/hash/slice'
require 'i18n'
require 'active_support/lazy_load_hooks'
rescue LoadError => e
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index f49ca47f14..cbc1608349 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -11,7 +11,7 @@ module ActiveSupport
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
# The Unicode version that is supported by the implementation
- UNICODE_VERSION = '6.1.0'
+ UNICODE_VERSION = '6.2.0'
# The default normalization used for operations that require
# normalization. It can be set to any of the normalizations
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 2191471daa..cc935e6cb9 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -580,7 +580,7 @@ module ActiveSupport
unit = case units
when Hash
- units[DECIMAL_UNITS[display_exponent]]
+ units[DECIMAL_UNITS[display_exponent]] || ''
when String, Symbol
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
else
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index dca91e8b75..d70d971538 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,8 +1,5 @@
require 'rbconfig'
-begin
- require 'minitest/parallel_each'
-rescue LoadError
-end
+require 'minitest/parallel_each'
module ActiveSupport
module Testing
@@ -48,39 +45,6 @@ module ActiveSupport
module Isolation
require 'thread'
- # Recent versions of MiniTest (such as the one shipped with Ruby 2.0) already define
- # a ParallelEach class.
- unless defined? ParallelEach
- class ParallelEach
- include Enumerable
-
- # default to 2 cores
- CORES = (ENV['TEST_CORES'] || 2).to_i
-
- def initialize list
- @list = list
- @queue = SizedQueue.new CORES
- end
-
- def grep pattern
- self.class.new super
- end
-
- def each
- threads = CORES.times.map {
- Thread.new {
- while job = @queue.pop
- yield job
- end
- }
- }
- @list.each { |i| @queue << i }
- CORES.times { @queue << nil }
- threads.each(&:join)
- end
- end
- end
-
def self.included(klass) #:nodoc:
klass.extend(Module.new {
def test_methods
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 0e6d12a186..98c866ac43 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -317,6 +317,10 @@ module ActiveSupport
end
alias_method :tv_sec, :to_i
+ def to_r
+ utc.to_r
+ end
+
# Return an instance of Time in the system timezone.
def to_time
utc.to_time
diff --git a/activesupport/lib/active_support/values/unicode_tables.dat b/activesupport/lib/active_support/values/unicode_tables.dat
index df17a8cccf..2571faa019 100644
--- a/activesupport/lib/active_support/values/unicode_tables.dat
+++ b/activesupport/lib/active_support/values/unicode_tables.dat
Binary files differ
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index 8a8f8f946d..ec0967fdd7 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -3,7 +3,7 @@ module ActiveSupport
MAJOR = 4
MINOR = 0
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 5158bbc196..ede08e23d5 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -672,6 +672,18 @@ class FileStoreTest < ActiveSupport::TestCase
end
end
+ def test_delete_does_not_delete_empty_parent_dir
+ sub_cache_dir = File.join(cache_dir, 'subdir/')
+ sub_cache_store = ActiveSupport::Cache::FileStore.new(sub_cache_dir)
+ assert_nothing_raised(Exception) do
+ assert sub_cache_store.write('foo', 'bar')
+ assert sub_cache_store.delete('foo')
+ end
+ assert File.exist?(cache_dir), "Parent of top level cache dir was deleted!"
+ assert File.exist?(sub_cache_dir), "Top level cache dir was deleted!"
+ assert Dir.entries(sub_cache_dir).reject {|f| ActiveSupport::Cache::FileStore::EXCLUDED_DIRS.include?(f)}.empty?
+ end
+
def test_log_exception_when_cache_read_fails
File.expects(:exist?).raises(StandardError, "failed")
@cache.send(:read_entry, "winston", {})
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 24e62cc2b9..7be578599b 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -88,6 +88,14 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal DateTime.civil(2005,2,4,19,59,59), DateTime.civil(2005,2,4,19,30,10).end_of_hour
end
+ def test_beginning_of_minute
+ assert_equal DateTime.civil(2005,2,4,19,30,0), DateTime.civil(2005,2,4,19,30,10).beginning_of_minute
+ end
+
+ def test_end_of_minute
+ assert_equal DateTime.civil(2005,2,4,19,30,59), DateTime.civil(2005,2,4,19,30,10).end_of_minute
+ end
+
def test_end_of_month
assert_equal DateTime.civil(2005,3,31,23,59,59), DateTime.civil(2005,3,20,10,10,10).end_of_month
assert_equal DateTime.civil(2005,2,28,23,59,59), DateTime.civil(2005,2,20,10,10,10).end_of_month
@@ -121,6 +129,9 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal DateTime.civil(2005,2,22,16), DateTime.civil(2005,2,22,15,15,10).change(:hour => 16)
assert_equal DateTime.civil(2005,2,22,16,45), DateTime.civil(2005,2,22,15,15,10).change(:hour => 16, :min => 45)
assert_equal DateTime.civil(2005,2,22,15,45), DateTime.civil(2005,2,22,15,15,10).change(:min => 45)
+
+ # datetime with fractions of a second
+ assert_equal DateTime.civil(2005,2,1,15,15,10.7), DateTime.civil(2005,2,22,15,15,10.7).change(:day => 1)
end
def test_advance
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 43c92003dc..2864d7a57f 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -121,6 +121,10 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,2,4,19,0,0), Time.local(2005,2,4,19,30,10).beginning_of_hour
end
+ def test_beginning_of_minute
+ assert_equal Time.local(2005,2,4,19,30,0), Time.local(2005,2,4,19,30,10).beginning_of_minute
+ end
+
def test_end_of_day
assert_equal Time.local(2007,8,12,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,12,10,10,10).end_of_day
with_env_tz 'US/Eastern' do
@@ -137,6 +141,10 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,2,4,19,59,59,Rational(999999999, 1000)), Time.local(2005,2,4,19,30,10).end_of_hour
end
+ def test_end_of_minute
+ assert_equal Time.local(2005,2,4,19,30,59,Rational(999999999, 1000)), Time.local(2005,2,4,19,30,10).end_of_minute
+ end
+
def test_last_year
assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).last_year
end
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index c2b3676aac..0f5699fd63 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -326,6 +326,17 @@ class TimeWithZoneTest < ActiveSupport::TestCase
assert_equal 946684800, twz.to_i
end
+ def test_to_r
+ result = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone['Hawaii']).to_r
+ assert_equal Rational(946684800, 1), result
+ assert_kind_of Rational, result
+ end
+
+ def test_time_at
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone['Hawaii'])
+ assert_equal time, Time.at(time)
+ end
+
def test_to_time
with_env_tz 'US/Eastern' do
assert_equal Time, @twz.to_time.class
@@ -539,6 +550,20 @@ class TimeWithZoneTest < ActiveSupport::TestCase
assert_equal "Fri, 31 Dec 1999 19:59:59 EST -05:00", twz.end_of_hour.inspect
end
+ def test_beginning_of_minute
+ utc = Time.utc(2000, 1, 1, 0, 30, 10)
+ twz = ActiveSupport::TimeWithZone.new(utc, @time_zone)
+ assert_equal "Fri, 31 Dec 1999 19:30:10 EST -05:00", twz.inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", twz.beginning_of_hour.inspect
+ end
+
+ def test_end_of_minute
+ utc = Time.utc(2000, 1, 1, 0, 30, 10)
+ twz = ActiveSupport::TimeWithZone.new(utc, @time_zone)
+ assert_equal "Fri, 31 Dec 1999 19:30:10 EST -05:00", twz.inspect
+ assert_equal "Fri, 31 Dec 1999 19:30:59 EST -05:00", twz.end_of_minute.inspect
+ end
+
def test_since
assert_equal "Fri, 31 Dec 1999 19:00:01 EST -05:00", @twz.since(1).inspect
end
diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb
index 75a0505899..0e3cf3b429 100644
--- a/activesupport/test/gzip_test.rb
+++ b/activesupport/test/gzip_test.rb
@@ -4,6 +4,12 @@ require 'active_support/core_ext/object/blank'
class GzipTest < ActiveSupport::TestCase
def test_compress_should_decompress_to_the_same_value
assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World"))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::NO_COMPRESSION))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::BEST_SPEED))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::BEST_COMPRESSION))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, Zlib::FILTERED))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, Zlib::HUFFMAN_ONLY))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, nil))
end
def test_compress_should_return_a_binary_string
@@ -12,4 +18,16 @@ class GzipTest < ActiveSupport::TestCase
assert_equal Encoding.find('binary'), compressed.encoding
assert !compressed.blank?, "a compressed blank string should not be blank"
end
+
+ def test_compress_should_return_gzipped_string_by_compression_level
+ source_string = "Hello World"*100
+
+ gzipped_by_speed = ActiveSupport::Gzip.compress(source_string, Zlib::BEST_SPEED)
+ assert_equal 1, Zlib::GzipReader.new(StringIO.new(gzipped_by_speed)).level
+
+ gzipped_by_best_compression = ActiveSupport::Gzip.compress(source_string, Zlib::BEST_COMPRESSION)
+ assert_equal 9, Zlib::GzipReader.new(StringIO.new(gzipped_by_best_compression)).level
+
+ assert_equal true, (gzipped_by_best_compression.bytesize < gzipped_by_speed.bytesize)
+ end
end
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index 5f54587f93..1fadef3637 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -301,6 +301,13 @@ module ActiveSupport
end
end
+ def test_number_to_human_with_custom_units_that_are_missing_the_needed_key
+ [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
+ assert_equal '123', number_helper.number_to_human(123, units: { thousand: 'k'})
+ assert_equal '123', number_helper.number_to_human(123, units: {})
+ end
+ end
+
def test_number_to_human_with_custom_format
[@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
assert_equal '123 times Thousand', number_helper.number_to_human(123456, :format => "%n times %u")
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index e9f7ff9d68..ee6e64b731 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1,5 +1,8 @@
## Rails 4.0.0 (unreleased) ##
+
+## Rails 4.0.0.beta1 (unreleased) ##
+
* Split Validations and Callbacks guide into two. *Steve Klabnik*
* New guide _Working with JavaScript in Rails_. *Steve Klabnik*
diff --git a/guides/code/getting_started/config/environments/development.rb b/guides/code/getting_started/config/environments/development.rb
index ed2667ac58..d169e9452c 100644
--- a/guides/code/getting_started/config/environments/development.rb
+++ b/guides/code/getting_started/config/environments/development.rb
@@ -22,10 +22,6 @@ Blog::Application.configure do
# Only use best-standards-support built into browsers.
config.action_dispatch.best_standards_support = :builtin
- # Log the query plan for queries taking more than this (works
- # with SQLite, MySQL, and PostgreSQL).
- config.active_record.auto_explain_threshold_in_seconds = 0.5
-
# Raise an error on page load if there are pending migrations
config.active_record.migration_error = :page_load
diff --git a/guides/code/getting_started/config/environments/production.rb b/guides/code/getting_started/config/environments/production.rb
index 58dab2a319..368a735122 100644
--- a/guides/code/getting_started/config/environments/production.rb
+++ b/guides/code/getting_started/config/environments/production.rb
@@ -72,10 +72,6 @@ Blog::Application.configure do
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
- # Log the query plan for queries taking more than this (works
- # with SQLite, MySQL, and PostgreSQL).
- # config.active_record.auto_explain_threshold_in_seconds = 0.5
-
# Disable automatic flushing of the log to improve performance.
# config.autoflush_log = false
diff --git a/guides/code/getting_started/config/routes.rb b/guides/code/getting_started/config/routes.rb
index 9950568629..0155b613a3 100644
--- a/guides/code/getting_started/config/routes.rb
+++ b/guides/code/getting_started/config/routes.rb
@@ -2,6 +2,6 @@ Blog::Application.routes.draw do
resources :posts do
resources :comments
end
-
- root to: "welcome#index"
+
+ root "welcome#index"
end
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index a678dd9d90..463da488f2 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -3,7 +3,7 @@ Ruby on Rails 4.0 Release Notes
Highlights in Rails 4.0:
-* Ruby 1.9.3 only
+* Ruby 2.0 preferred; 1.9.3+ required
* Strong Parameters
* Turbolinks
* Russian Doll Caching
@@ -200,6 +200,8 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ
* Remove IdentityMap.
+* Remove automatic execution of EXPLAIN queries. The option `active_record.auto_explain_threshold_in_seconds` is no longer used and should be removed.
+
* Adds `ActiveRecord::NullRelation` and `ActiveRecord::Relation#none` implementing the null object pattern for the Relation class.
* Added `create_join_table` migration helper to create HABTM join tables.
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 7260a48c8c..da155628f3 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -116,9 +116,9 @@ When this form is submitted, the value of `params[:client]` will be `{"name" =>
Note that the `params` hash is actually an instance of `ActiveSupport::HashWithIndifferentAccess`, which acts like a hash that lets you use symbols and strings interchangeably as keys.
-### JSON/XML parameters
+### JSON parameters
-If you're writing a web service application, you might find yourself more comfortable on accepting parameters in JSON or XML format. Rails will automatically convert your parameters into `params` hash, which you'll be able to access like you would normally do with form data.
+If you're writing a web service application, you might find yourself more comfortable on accepting parameters in JSON format. Rails will automatically convert your parameters into `params` hash, which you'll be able to access like you would normally do with form data.
So for example, if you are sending this JSON parameter:
@@ -128,7 +128,7 @@ So for example, if you are sending this JSON parameter:
You'll get `params[:company]` as `{ :name => "acme", "address" => "123 Carrot Street" }`.
-Also, if you've turned on `config.wrap_parameters` in your initializer or calling `wrap_parameters` in your controller, you can safely omit the root element in the JSON/XML parameter. The parameters will be cloned and wrapped in the key according to your controller's name by default. So the above parameter can be written as:
+Also, if you've turned on `config.wrap_parameters` in your initializer or calling `wrap_parameters` in your controller, you can safely omit the root element in the JSON parameter. The parameters will be cloned and wrapped in the key according to your controller's name by default. So the above parameter can be written as:
```json
{ "name": "acme", "address": "123 Carrot Street" }
@@ -142,6 +142,8 @@ And assume that you're sending the data to `CompaniesController`, it would then
You can customize the name of the key or specific parameters you want to wrap by consulting the [API documentation](http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html)
+NOTE: A support for parsing XML parameters has been extracted into a gem named `actionpack-xml_parser`
+
### Routing Parameters
The `params` hash will always contain the `:controller` and `:action` keys, but you should use the methods `controller_name` and `action_name` instead to access these values. Any other parameters defined by the routing, such as `:id` will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the `:status` parameter in a "pretty" URL:
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 513ae1272f..8720aae169 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -116,7 +116,7 @@ Setting this up is painfully simple.
First off, we need to create a simple `User` scaffold:
```bash
-$ rails generate scaffold user name:string email:string login:string
+$ rails generate scaffold user name email login
$ rake db:migrate
```
@@ -447,7 +447,7 @@ end
Action Mailer Callbacks
---------------------------
-Action Mailer allows for you to specify a `before_action`, `after_action` and 'around_action'.
+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.
@@ -507,7 +507,6 @@ The following configuration options are best made in one of the environment file
| Configuration | Description |
|---------------|-------------|
-|`template_root`|Determines the base from which template references will be made.|
|`logger`|Generates information on the mailing run if available. Can be set to `nil` for no logging. Compatible with both Ruby's own `Logger` and `Log4r` loggers.|
|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.</li><li>`:enable_starttls_auto` - Set this to `false` if there is a problem with your server certificate that you cannot resolve.</li></ul>|
|`sendmail_settings`|Allows you to override options for the `:sendmail` delivery method.<ul><li>`:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.</li><li>`:arguments` - The command line arguments to be passed to sendmail. Defaults to `-i -t`.</li></ul>|
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 516457bcd3..bb42fab101 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -342,19 +342,17 @@ By using the `after_commit` callback we can account for this case.
```ruby
class PictureFile < ActiveRecord::Base
- attr_accessor :delete_file
+ after_commit :delete_picture_file_from_disk, :on => [:destroy]
- after_destroy do |picture_file|
- picture_file.delete_file = picture_file.filepath
- end
-
- after_commit do |picture_file|
- if picture_file.delete_file && File.exist?(picture_file.delete_file)
- File.delete(picture_file.delete_file)
- picture_file.delete_file = nil
+ def delete_picture_file_from_disk
+ if File.exist?(filepath)
+ File.delete(filepath)
end
end
end
```
+NOTE: the `:on` option specifies when a callback will be fired. If you
+don't supply the `:on` option the callback will fire for every action.
+
The `after_commit` and `after_rollback` callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don't interfere with the other callbacks. As such, if your callback code could raise an exception, you'll need to rescue it and handle it appropriately within the callback.
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 62d6294ae5..5d82541da9 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -299,7 +299,7 @@ Client.first(2)
The SQL equivalent of the above is:
```sql
-SELECT * FROM clients LIMIT 2
+SELECT * FROM clients ORDER BY id ASC LIMIT 2
```
#### last
@@ -315,7 +315,7 @@ Client.last(2)
The SQL equivalent of the above is:
```sql
-SELECT * FROM clients ORDER By id DESC LIMIT 2
+SELECT * FROM clients ORDER BY id DESC LIMIT 2
```
### Retrieving Multiple Objects in Batches
@@ -1609,45 +1609,6 @@ EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1)
under MySQL.
-### Automatic EXPLAIN
-
-Active Record is able to run EXPLAIN automatically on slow queries and log its
-output. This feature is controlled by the configuration parameter
-
-```ruby
-config.active_record.auto_explain_threshold_in_seconds
-```
-
-If set to a number, any query exceeding those many seconds will have its EXPLAIN
-automatically triggered and logged. In the case of relations, the threshold is
-compared to the total time needed to fetch records. So, a relation is seen as a
-unit of work, no matter whether the implementation of eager loading involves
-several queries under the hood.
-
-A threshold of `nil` disables automatic EXPLAINs.
-
-The default threshold in development mode is 0.5 seconds, and `nil` in test and
-production modes.
-
-INFO. Automatic EXPLAIN gets disabled if Active Record has no logger, regardless
-of the value of the threshold.
-
-#### Disabling Automatic EXPLAIN
-
-Automatic EXPLAIN can be selectively silenced with `ActiveRecord::Base.silence_auto_explain`:
-
-```ruby
-ActiveRecord::Base.silence_auto_explain do
- # no automatic EXPLAIN is triggered here
-end
-```
-
-That may be useful for queries you know are slow but fine, like a heavyweight
-report of an admin interface.
-
-As its name suggests, `silence_auto_explain` only silences automatic EXPLAINs.
-Explicit calls to `ActiveRecord::Relation#explain` run.
-
### Interpreting EXPLAIN
Interpretation of the output of EXPLAIN is beyond the scope of this guide. The
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index eaa47d4ebf..32641d04c1 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -618,6 +618,35 @@ class GoodnessValidator < ActiveModel::Validator
end
```
+Note that the validator will be initialized *only once* for the whole application
+life cycle, and not on each validation run, so be careful about using instance
+variables inside it.
+
+If your validator is complex enough that you want instance variables, you can
+easily use a plain old Ruby object instead:
+
+```ruby
+class Person < ActiveRecord::Base
+ validate do |person|
+ GoodnessValidator.new(person).validate
+ end
+end
+
+class GoodnessValidator
+ def initialize(person)
+ @person = person
+ end
+
+ def validate
+ if some_complex_condition_involving_ivars_and_private_methods?
+ @person.errors[:base] << "This person is evil"
+ end
+ end
+
+ # …
+end
+```
+
### `validates_each`
This helper validates attributes against a block. It doesn't have a predefined
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index f02b377832..517db0d222 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -3320,7 +3320,25 @@ date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010
`beginning_of_hour` is aliased to `at_beginning_of_hour`.
-INFO: `beginning_of_hour` and `end_of_hour` are implemented for `Time` and `DateTime` but **not** `Date` as it does not make sense to request the beginning or end of an hour on a `Date` instance.
+##### `beginning_of_minute`, `end_of_minute`
+
+The method `beginning_of_minute` returns a timestamp at the beginning of the minute (hh:mm:00):
+
+```ruby
+date = DateTime.new(2010, 6, 7, 19, 55, 25)
+date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010
+```
+
+The method `end_of_minute` returns a timestamp at the end of the minute (hh:mm:59):
+
+```ruby
+date = DateTime.new(2010, 6, 7, 19, 55, 25)
+date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010
+```
+
+`beginning_of_minute` is aliased to `at_beginning_of_minute`.
+
+INFO: `beginning_of_hour`, `end_of_hour`, `beginning_of_minute` and `end_of_minute` are implemented for `Time` and `DateTime` but **not** `Date` as it does not make sense to request the beginning or end of an hour or minute on a `Date` instance.
##### `ago`, `since`
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index a270ec7a7e..abab3dd983 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -343,8 +343,3 @@ class ProductsController < ApplicationController
end
end
```
-
-Further reading
----------------
-
-* [Scaling Rails Screencasts](http://railslab.newrelic.com/scaling-rails)
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index be46e15078..dbbeec7126 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -268,8 +268,6 @@ config.middleware.delete "Rack::MethodOverride"
* `config.active_record.lock_optimistically` controls whether Active Record will use optimistic locking and is true by default.
-* `config.active_record.auto_explain_threshold_in_seconds` configures the threshold for automatic EXPLAINs (`nil` disables this feature). Queries exceeding the threshold get their query plan logged. Default is 0.5 in development mode.
-
* +config.active_record.cache_timestamp_format+ controls the format of the timestamp value in the cache key. Default is +:number+.
The MySQL adapter adds one additional configuration option:
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index baef620c6c..7909a00c47 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -85,7 +85,7 @@ You can also run any single test separately:
$ ARCONN=sqlite3 ruby -Itest test/cases/associations/has_many_associations_test.rb
```
-You can invoke `test_jdbcmysql`, `test_jdbcsqlite3` or `test_jdbcpostgresql` also. See the file `activerecord/RUNNING_UNIT_TESTS` for information on running more targeted database tests, or the file `ci/travis.rb` for the test suite run by the continuous integration server.
+You can invoke `test_jdbcmysql`, `test_jdbcsqlite3` or `test_jdbcpostgresql` also. See the file `activerecord/RUNNING_UNIT_TESTS.rdoc` for information on running more targeted database tests, or the file `ci/travis.rb` for the test suite run by the continuous integration server.
### Warnings
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 58c6870d4a..00939c4ff2 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -57,7 +57,7 @@ The `--full` option tells the generator that you want to create an engine, inclu
end
```
* A file at `lib/blorgh/engine.rb` which is identical in function to a standard Rails application's `config/application.rb` file:
-
+
```ruby
module Blorgh
class Engine < ::Rails::Engine
@@ -72,12 +72,12 @@ The `--mountable` option tells the generator that you want to create a "mountabl
* A namespaced `ApplicationHelper` stub
* A layout view template for the engine
* Namespace isolation to `config/routes.rb`:
-
+
```ruby
Blorgh::Engine.routes.draw do
end
```
-
+
* Namespace isolation to `lib/blorgh/engine.rb`:
```ruby
@@ -395,7 +395,7 @@ def create
@post = Post.find(params[:post_id])
@comment = @post.comments.create(params[:comment])
flash[:notice] = "Comment has been created!"
- redirect_to post_path
+ redirect_to posts_path
end
```
@@ -650,6 +650,14 @@ self.author = Blorgh.user_class.find_or_create_by(name: author_name)
Resulting in something a little shorter, and more implicit in its behavior. The `user_class` method should always return a `Class` object.
+Since we changed the `user_class` method to no longer return a
+`String` but a `Class` we must also modify our `belongs_to` definition
+in the `Blorgh::Post` model:
+
+```ruby
+belongs_to :author, class_name: Blorgh.user_class.to_s
+```
+
To set this configuration setting within the application, an initializer should be used. By using an initializer, the configuration will be set up before the application starts and calls the engine's models which may depend on this configuration setting existing.
Create a new initializer at `config/initializers/blorgh.rb` inside the application where the `blorgh` engine is installed and put this content in it:
@@ -789,7 +797,7 @@ module Blorgh::Concerns::Models::Post
extend ActiveSupport::Concern
# 'included do' causes the included code to be evaluated in the
- # context where it is included (post.rb), rather than be
+ # context where it is included (post.rb), rather than be
# executed in the module's context (blorgh/concerns/models/post).
included do
attr_accessor :author_name
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 1a08eb420a..d7c789e2d8 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -412,7 +412,7 @@ This command will generate the `Thud` application, and then apply the template t
Templates don't have to be stored on the local system, the `-m` option also supports online templates:
```bash
-$ rails new thud -m https://gist.github.com/722911.txt
+$ rails new thud -m https://gist.github.com/radar/722911/raw/
```
Whilst the final section of this guide doesn't cover how to generate the most awesome template known to man, it will take you through the methods available at your disposal so that you can develop it yourself. These same methods are also available for generators.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index c394f30c38..87f5e43157 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -21,7 +21,7 @@ application from scratch. It does not assume that you have any prior experience
with Rails. However, to get the most out of it, you need to have some
prerequisites installed:
-* The [Ruby](http://www.ruby-lang.org/en/downloads) language version 1.9.3 or higher
+* The [Ruby](http://www.ruby-lang.org/en/downloads) language version 1.9.3 or newer
* The [RubyGems](http://rubygems.org/) packaging system
* To learn more about RubyGems, please read the [RubyGems User Guide](http://docs.rubygems.org/read/book/1)
* A working installation of the [SQLite3 Database](http://www.sqlite.org)
@@ -84,7 +84,7 @@ current version of Ruby installed:
```bash
$ ruby -v
-ruby 1.9.3p327
+ruby 1.9.3p385
```
To install Rails, use the `gem install` command provided by RubyGems:
diff --git a/guides/source/kindle/KINDLE.md b/guides/source/kindle/KINDLE.md
index 08937e053e..8c4fad18aa 100644
--- a/guides/source/kindle/KINDLE.md
+++ b/guides/source/kindle/KINDLE.md
@@ -10,7 +10,7 @@
## Resources
* [Stack Overflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format)
- * Example Periodical [.ncx](https://gist.github.com/808c971ed087b839d462) and [.opf](https://gist.github.com/d6349aa8488eca2ee6d0)
+ * Example Periodical [.ncx](https://gist.github.com/mipearson/808c971ed087b839d462) and [.opf](https://gist.github.com/mipearson/d6349aa8488eca2ee6d0)
* [Kindle Publishing Guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf)
* [KindleGen & Kindle Previewer](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621)
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 339008ab9e..bfd1a7c61b 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -568,7 +568,8 @@ def show
@book = Book.find_by_id(params[:id])
if @book.nil?
@books = Book.all
- render "index", alert: "Your book was not found!"
+ flash[:alert] = "Your book was not found"
+ render "index"
end
end
```
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index cefbc3b829..d738d847e9 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -344,6 +344,16 @@ create_join_table :products, :categories, column_options: {null: true}
will create the `product_id` and `category_id` with the `:null` option as
`true`.
+`create_join_table` also accepts a block, which you can use to add indices
+(which are not created by default) or additional columns:
+
+```ruby
+create_join_table :products, :categories do |t|
+ t.index :products
+ t.index :categories
+end
+```
+
### Changing Tables
A close cousin of `create_table` is `change_table`, used for changing existing
@@ -444,7 +454,7 @@ class ExampleMigration < ActiveRecord::Migration
end
```
-Using `reversible` will insure that the instructions are executed in the
+Using `reversible` will ensure that the instructions are executed in the
right order too. If the previous example migration is reverted,
the `down` block will be run after the `home_page_url` column is removed and
right before the table `products` is dropped.
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 4614169653..d7a4a237ed 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -155,7 +155,7 @@ creates six different routes in your application, all mapping to the `Geocoders`
| PATCH/PUT | /geocoder | update | update the one and only geocoder resource |
| DELETE | /geocoder | destroy | delete the geocoder resource |
-NOTE: Because you might want to use the same controller for a singular route (`/account`) and a plural route (`/accounts/45`), singular resources map to plural controllers.
+NOTE: Because you might want to use the same controller for a singular route (`/account`) and a plural route (`/accounts/45`), singular resources map to plural controllers. So that, for example, `resource :photo` and `resources :photos` creates both singular and plural routes that map to the same controller (`PhotosController`).
A singular resourceful route generates these helpers:
@@ -797,6 +797,16 @@ You should put the `root` route at the top of the file, because it is the most p
NOTE: The `root` route only routes `GET` requests to the action.
+You can also use root inside namespaces and scopes as well. For example:
+
+```ruby
+namespace :admin do
+ root to: "admin#index"
+end
+
+root to: "home#index"
+```
+
### Unicode character routes
You can specify unicode character routes directly. For example:
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 39a44794a7..540197e6e7 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -774,10 +774,13 @@ You don't need to set up and run your tests by hand on a test-by-test basis. Rai
Brief Note About `Test::Unit`
-----------------------------
-Ruby ships with a boat load of libraries. One little gem of a library is `Test::Unit`, a framework for unit testing in Ruby. All the basic assertions discussed above are actually defined in `Test::Unit::Assertions`. The class `ActiveSupport::TestCase` which we have been using in our unit and functional tests extends `Test::Unit::TestCase`, allowing
+Ruby ships with a boat load of libraries. Ruby 1.8 provides `Test::Unit`, a framework for unit testing in Ruby. All the basic assertions discussed above are actually defined in `Test::Unit::Assertions`. The class `ActiveSupport::TestCase` which we have been using in our unit and functional tests extends `Test::Unit::TestCase`, allowing
us to use all of the basic assertions in our tests.
+Ruby 1.9 introduced `MiniTest`, an updated version of `Test::Unit` which provides a backwards compatible API for `Test::Unit`. You could also use `MiniTest` in Ruby 1.8 by installing the `minitest` gem.
+
NOTE: For more information on `Test::Unit`, refer to [test/unit Documentation](http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/)
+For more information on `MiniTest`, refer to [Minitest](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/minitest/unit/rdoc/)
Setup and Teardown
------------------
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 59e21e9be2..52f5232efc 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -16,11 +16,11 @@ The best way to be sure that your application still works after upgrading is to
Rails generally stays close to the latest released Ruby version when it's released:
-* Rails 3 and above requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible.
-* Rails 3.2.x will be the last branch to support Ruby 1.8.7.
-* Rails 4 will support only Ruby 1.9.3.
+* Rails 3 and above require Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially. You should upgrade as early as possible.
+* Rails 3.2.x is the last branch to support Ruby 1.8.7.
+* Rails 4 prefers Ruby 2.0 and requires 1.9.3 or newer.
-TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump on to 1.9.2 or 1.9.3 for smooth sailing.
+TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing.
Upgrading from Rails 3.2 to Rails 4.0
-------------------------------------
@@ -45,6 +45,12 @@ Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must rep
* Rails 4.0 has changed `serialized_attributes` and `attr_readonly` to class methods only. Now you shouldn't use instance methods, it's deprecated. You must change them, e.g. `self.serialized_attributes` to `self.class.serialized_attributes`.
+* Rails 4.0 has removed `attr_accessible` and `attr_protected` feature in favor of Strong Parameters. You can use the [Protected Attributes gem](https://github.com/rails/protected_attributes) to a smoothly upgrade path.
+
+### Active Resource
+
+Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your Gemfile.
+
### Active Model
* Rails 4.0 has changed how errors attach with the `ActiveModel::Validations::ConfirmationValidator`. Now when confirmation validations fail the error will be attached to `:#{attribute}_confirmation` instead of `attribute`.
@@ -84,6 +90,17 @@ get 'こんにちは', controller: 'welcome', action: 'index'
* Rails 4.0 has removed ActionDispatch::BestStandardsSupport middleware, !DOCTYPE html already triggers standards mode per http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers`
+Remember you must also remove any references to the middleware from your application code, for example:
+
+```ruby
+# Raise exception
+config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport)
+```
+
+Also check your environment settings for `config.action_dispatch.best_standards_support` and remove it if present.
+
+* In Rails 4.0, precompiling assets no longer automatically copies non-JS/CSS assets from `vendor/assets` and `lib/assets`. Rails application and engine developers should put these assets in `app/assets` or configure `config.assets.precompile`.
+
### Active Support
Rails 4.0 removes the `j` alias for `ERB::Util#json_escape` since `j` is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`.
@@ -97,14 +114,14 @@ Upgrading from Rails 3.1 to Rails 3.2
If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2.
-The following changes are meant for upgrading your application to Rails 3.2.2, the latest 3.2.x version of Rails.
+The following changes are meant for upgrading your application to Rails 3.2.12, the latest 3.2.x version of Rails.
### Gemfile
Make the following changes to your `Gemfile`.
```ruby
-gem 'rails', '= 3.2.2'
+gem 'rails', '= 3.2.12'
group :assets do
gem 'sass-rails', '~> 3.2.3'
@@ -144,14 +161,14 @@ Upgrading from Rails 3.0 to Rails 3.1
If your application is currently on any version of Rails older than 3.0.x, you should upgrade to Rails 3.0 before attempting an update to Rails 3.1.
-The following changes are meant for upgrading your application to Rails 3.1.3, the latest 3.1.x version of Rails.
+The following changes are meant for upgrading your application to Rails 3.1.11, the latest 3.1.x version of Rails.
### Gemfile
Make the following changes to your `Gemfile`.
```ruby
-gem 'rails', '= 3.1.3'
+gem 'rails', '= 3.1.11'
gem 'mysql2'
# Needed for the new asset pipeline
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index 03ef770352..7c4192ee26 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -111,7 +111,7 @@ paintIt = (element, backgroundColor, textColor) ->
element.style.color = textColor
$ ->
- $("a[data-color]").click ->
+ $("a[data-background-color]").click ->
backgroundColor = $(this).data("background-color")
textColor = $(this).data("text-color")
paintIt(this, backgroundColor, textColor)
diff --git a/install.rb b/install.rb
index 3967624a3f..77c3e6048a 100644
--- a/install.rb
+++ b/install.rb
@@ -7,10 +7,10 @@ end
%w( activesupport activemodel activerecord actionpack actionmailer railties ).each do |framework|
puts "Installing #{framework}..."
- `cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --local --no-ri --no-rdoc && rm #{framework}-#{version}.gem`
+ `cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --no-ri --no-rdoc && rm #{framework}-#{version}.gem`
end
-puts "Installing Rails..."
+puts "Installing rails..."
`gem build rails.gemspec`
-`gem install rails-#{version}.gem --local --no-ri --no-rdoc `
+`gem install rails-#{version}.gem --no-ri --no-rdoc `
`rm rails-#{version}.gem`
diff --git a/rails.gemspec b/rails.gemspec
index 34957c9455..8a5d042361 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -26,6 +26,6 @@ Gem::Specification.new do |s|
s.add_dependency 'actionmailer', version
s.add_dependency 'railties', version
- s.add_dependency 'bundler', '>= 1.2.2', '< 2.0'
- s.add_dependency 'sprockets-rails', '~> 2.0.0.rc1'
+ s.add_dependency 'bundler', '>= 1.3.0', '< 2.0'
+ s.add_dependency 'sprockets-rails', '~> 2.0.0.rc3'
end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index f86baee4c3..f3c07470af 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,11 +1,40 @@
## Rails 4.0.0 (unreleased) ##
-* Add --rc option to support the load of a custom rc file during the generation of a new app.
+
+## Rails 4.0.0.beta1 (February 25, 2013) ##
+
+* Improve `rake stats` for JavaScript and CoffeeScript: ignore block comments
+ and calculates number of functions.
+
+ *Hendy Tanata*
+
+* Ability to use a custom builder by passing `--builder` (or `-b`) has been removed. Consider
+ using application template instead. See this guide for more detail:
+ http://guides.rubyonrails.org/rails_application_templates.html
+
+ *Prem Sichanugrist*
+
+* fix rake db:* tasks to work with DATABASE_URL and without config/database.yml
+
+ *Terence Lee*
+
+* Add notice message for destroy action in scaffold generator.
+
+ *Rahul P. Chaudhari*
+
+* Add two new test rake tasks to speed up full test runs.
+
+ * `test:all`: run tests quickly by merging all types and not resetting db.
+ * `test:all:db`: run tests quickly, but also reset db.
+
+ *Ryan Davis*
+
+* Add `--rc` option to support the load of a custom rc file during the generation of a new app.
*Amparo Luna*
-* Add --no-rc option to skip the loading of railsrc file during the generation of a new app.
-
+* Add `--no-rc` option to skip the loading of railsrc file during the generation of a new app.
+
*Amparo Luna*
* Fixes database.yml when creating a new rails application with '.'
@@ -13,15 +42,6 @@
*Jeremy W. Rowe*
-* Deprecate the `eager_load_paths` configuration and alias it to `autoload_paths`.
- Since the default in Rails 4.0 is to run in 'threadsafe' mode we need to eager
- load all of the paths in `autoload_paths`. This may have unintended consequences
- if you have added 'lib' to `autoload_paths` such as loading unneeded code or
- code intended only for development and/or test environments. If this applies to
- your application you should thoroughly check what is being eager loaded.
-
- *Andrew White*
-
* Restore Rails::Engine::Railties#engines with deprecation to ensure
compatibility with gems such as Thinking Sphinx
Fix #8551
diff --git a/railties/lib/rails/app_rails_loader.rb b/railties/lib/rails/app_rails_loader.rb
index 8937e10db3..44f4d3dabc 100644
--- a/railties/lib/rails/app_rails_loader.rb
+++ b/railties/lib/rails/app_rails_loader.rb
@@ -3,12 +3,16 @@ require 'pathname'
module Rails
module AppRailsLoader
RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
- EXECUTABLE = 'bin/rails'
+ EXECUTABLES = ['bin/rails', 'script/rails']
def self.exec_app_rails
- cwd = Dir.pwd
- return unless in_rails_application_or_engine? || in_rails_application_or_engine_subdirectory?
- exec RUBY, EXECUTABLE, *ARGV if in_rails_application_or_engine?
+ cwd = Dir.pwd
+
+ exe = find_executable
+ exe ||= find_executable_in_parent_path
+ return unless exe
+
+ exec RUBY, exe, *ARGV if find_executable
Dir.chdir("..") do
# Recurse in a chdir block: if the search fails we want to be sure
# the application is generated in the original working directory.
@@ -18,12 +22,16 @@ module Rails
# could not chdir, no problem just return
end
- def self.in_rails_application_or_engine?
- File.exists?(EXECUTABLE) && File.read(EXECUTABLE) =~ /(APP|ENGINE)_PATH/
+ def self.find_executable
+ EXECUTABLES.find do |exe|
+ File.exists?(exe) && File.read(exe) =~ /(APP|ENGINE)_PATH/
+ end
end
- def self.in_rails_application_or_engine_subdirectory?(path = Pathname.new(Dir.pwd))
- File.exists?(File.join(path, EXECUTABLE)) || !path.root? && in_rails_application_or_engine_subdirectory?(path.parent)
+ def self.find_executable_in_parent_path(path = Pathname.new(Dir.pwd))
+ EXECUTABLES.find do |exe|
+ File.exists?(File.join(path, exe)) || !path.root? && find_executable_in_parent_path(path.parent)
+ end
end
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 2c7ddd86e7..17763b39c5 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -97,12 +97,16 @@ module Rails
self
end
- # Loads and returns the contents of the #database_configuration_file. The
- # contents of the file are processed via ERB before being sent through
- # YAML::load.
+ # Loads and returns the configuration of the database.
+ # First, looks at If ENV['DATABASE_URL'] if it's not present it uses the #paths["config/database"]
+ # The contents of the file are processed via ERB before being sent through YAML::load.
def database_configuration
- require 'erb'
- YAML.load ERB.new(IO.read(paths["config/database"].first)).result
+ if ENV['DATABASE_URL']
+ {Rails.env => ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.connection_url_to_hash(ENV['DATABASE_URL']).stringify_keys}
+ else
+ require 'erb'
+ YAML.load ERB.new(IO.read(paths["config/database"].first)).result
+ end
rescue Psych::SyntaxError => e
raise "YAML syntax error occurred while parsing #{paths["config/database"].first}. " \
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index 872d78d9a4..3ae60312c5 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -89,13 +89,6 @@ module Rails
ActionDispatch::Reloader.to_cleanup(&callback)
end
end
-
- # Disable dependency loading during request cycle
- initializer :disable_dependency_loading do
- if config.eager_load && config.cache_classes
- ActiveSupport::Dependencies.unhook!
- end
- end
end
end
end
diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb
index b717b026de..e5341ac436 100644
--- a/railties/lib/rails/cli.rb
+++ b/railties/lib/rails/cli.rb
@@ -3,9 +3,6 @@ require 'rails/app_rails_loader'
# If we are inside a Rails application this method performs an exec and thus
# the rest of this script is not run.
-#
-# TODO: when we hit this, advise adding ./bin to $PATH instead. Then the
-# app's `rails` executable is run immediately.
Rails::AppRailsLoader.exec_app_rails
require 'rails/ruby_version_check'
diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb
index 039360fcf6..0ae6d2a455 100644
--- a/railties/lib/rails/code_statistics.rb
+++ b/railties/lib/rails/code_statistics.rb
@@ -1,3 +1,5 @@
+require 'rails/code_statistics_calculator'
+
class CodeStatistics #:nodoc:
TEST_TYPES = ['Controller tests',
@@ -33,64 +35,38 @@ class CodeStatistics #:nodoc:
end
def calculate_directory_statistics(directory, pattern = /.*\.(rb|js|coffee)$/)
- stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
+ stats = CodeStatisticsCalculator.new
Dir.foreach(directory) do |file_name|
- if File.directory?(directory + "/" + file_name) and (/^\./ !~ file_name)
- newstats = calculate_directory_statistics(directory + "/" + file_name, pattern)
- stats.each { |k, v| stats[k] += newstats[k] }
+ path = "#{directory}/#{file_name}"
+
+ if File.directory?(path) && (/^\./ !~ file_name)
+ stats.add(calculate_directory_statistics(path, pattern))
end
next unless file_name =~ pattern
- comment_started = false
-
- case file_name
- when /.*\.js$/
- comment_pattern = /^\s*\/\//
- else
- comment_pattern = /^\s*#/
- end
-
- File.open(directory + "/" + file_name) do |f|
- while line = f.gets
- stats["lines"] += 1
- if(comment_started)
- if line =~ /^=end/
- comment_started = false
- end
- next
- else
- if line =~ /^=begin/
- comment_started = true
- next
- end
- end
- stats["classes"] += 1 if line =~ /^\s*class\s+[_A-Z]/
- stats["methods"] += 1 if line =~ /^\s*def\s+[_a-z]/
- stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ comment_pattern
- end
- end
+ stats.add_by_file_path(path)
end
stats
end
def calculate_total
- total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
- @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
- total
+ @statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, total|
+ total.add(pair.last)
+ end
end
def calculate_code
code_loc = 0
- @statistics.each { |k, v| code_loc += v['codelines'] unless TEST_TYPES.include? k }
+ @statistics.each { |k, v| code_loc += v.code_lines unless TEST_TYPES.include? k }
code_loc
end
def calculate_tests
test_loc = 0
- @statistics.each { |k, v| test_loc += v['codelines'] if TEST_TYPES.include? k }
+ @statistics.each { |k, v| test_loc += v.code_lines if TEST_TYPES.include? k }
test_loc
end
@@ -105,15 +81,15 @@ class CodeStatistics #:nodoc:
end
def print_line(name, statistics)
- m_over_c = (statistics["methods"] / statistics["classes"]) rescue m_over_c = 0
- loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0
-
- puts "| #{name.ljust(20)} " +
- "| #{statistics["lines"].to_s.rjust(5)} " +
- "| #{statistics["codelines"].to_s.rjust(5)} " +
- "| #{statistics["classes"].to_s.rjust(7)} " +
- "| #{statistics["methods"].to_s.rjust(7)} " +
- "| #{m_over_c.to_s.rjust(3)} " +
+ m_over_c = (statistics.methods / statistics.classes) rescue m_over_c = 0
+ loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue loc_over_m = 0
+
+ puts "| #{name.ljust(20)} " \
+ "| #{statistics.lines.to_s.rjust(5)} " \
+ "| #{statistics.code_lines.to_s.rjust(5)} " \
+ "| #{statistics.classes.to_s.rjust(7)} " \
+ "| #{statistics.methods.to_s.rjust(7)} " \
+ "| #{m_over_c.to_s.rjust(3)} " \
"| #{loc_over_m.to_s.rjust(5)} |"
end
diff --git a/railties/lib/rails/code_statistics_calculator.rb b/railties/lib/rails/code_statistics_calculator.rb
new file mode 100644
index 0000000000..60e4aef9b7
--- /dev/null
+++ b/railties/lib/rails/code_statistics_calculator.rb
@@ -0,0 +1,79 @@
+class CodeStatisticsCalculator #:nodoc:
+ attr_reader :lines, :code_lines, :classes, :methods
+
+ PATTERNS = {
+ rb: {
+ line_comment: /^\s*#/,
+ begin_block_comment: /^=begin/,
+ end_block_comment: /^=end/,
+ class: /^\s*class\s+[_A-Z]/,
+ method: /^\s*def\s+[_a-z]/,
+ },
+ js: {
+ line_comment: %r{^\s*//},
+ begin_block_comment: %r{^\s*/\*},
+ end_block_comment: %r{\*/},
+ method: /function(\s+[_a-zA-Z][\da-zA-Z]*)?\s*\(/,
+ },
+ coffee: {
+ line_comment: /^\s*#/,
+ begin_block_comment: /^\s*###/,
+ end_block_comment: /^\s*###/,
+ class: /^\s*class\s+[_A-Z]/,
+ method: /[-=]>/,
+ }
+ }
+
+ def initialize(lines = 0, code_lines = 0, classes = 0, methods = 0)
+ @lines = lines
+ @code_lines = code_lines
+ @classes = classes
+ @methods = methods
+ end
+
+ def add(code_statistics_calculator)
+ @lines += code_statistics_calculator.lines
+ @code_lines += code_statistics_calculator.code_lines
+ @classes += code_statistics_calculator.classes
+ @methods += code_statistics_calculator.methods
+ end
+
+ def add_by_file_path(file_path)
+ File.open(file_path) do |f|
+ self.add_by_io(f, file_type(file_path))
+ end
+ end
+
+ def add_by_io(io, file_type)
+ patterns = PATTERNS[file_type] || {}
+
+ comment_started = false
+
+ while line = io.gets
+ @lines += 1
+
+ if comment_started
+ if patterns[:end_block_comment] && line =~ patterns[:end_block_comment]
+ comment_started = false
+ end
+ next
+ else
+ if patterns[:begin_block_comment] && line =~ patterns[:begin_block_comment]
+ comment_started = true
+ next
+ end
+ end
+
+ @classes += 1 if patterns[:class] && line =~ patterns[:class]
+ @methods += 1 if patterns[:method] && line =~ patterns[:method]
+ if line !~ /^\s*$/ && (patterns[:line_comment].nil? || line !~ patterns[:line_comment])
+ @code_lines += 1
+ end
+ end
+ end
+
+ private
+ def file_type(file_path)
+ File.extname(file_path).sub(/\A\./, '').downcase.to_sym
+ end
+end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 8a6d1f34aa..46a6485c44 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -34,8 +34,9 @@ module Rails
# == Configuration
#
# Besides the +Railtie+ configuration which is shared across the application, in a
- # <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt> and <tt>autoload_once_paths</tt>,
- # which, differently from a <tt>Railtie</tt>, are scoped to the current engine.
+ # <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt>, <tt>eager_load_paths</tt>
+ # and <tt>autoload_once_paths</tt>, which, differently from a <tt>Railtie</tt>, are scoped to
+ # the current engine.
#
# class MyEngine < Rails::Engine
# # Add a load path for this specific Engine
@@ -455,9 +456,9 @@ module Rails
end
# Eager load the application by loading all ruby
- # files inside autoload_paths.
+ # files inside eager_load paths.
def eager_load!
- config.autoload_paths.each do |load_path|
+ config.eager_load_paths.each do |load_path|
matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
require_dependency file.sub(matcher, '\1')
@@ -557,6 +558,7 @@ module Rails
# Freeze so future modifications will fail rather than do nothing mysteriously
config.autoload_paths.freeze
+ config.eager_load_paths.freeze
config.autoload_once_paths.freeze
end
@@ -669,7 +671,7 @@ module Rails
end
def _all_autoload_paths #:nodoc:
- @_all_autoload_paths ||= (config.autoload_paths + config.autoload_once_paths).uniq
+ @_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
end
def _all_load_paths #:nodoc:
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 2b23d8be38..10d1821709 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -4,7 +4,7 @@ module Rails
class Engine
class Configuration < ::Rails::Railtie::Configuration
attr_reader :root
- attr_writer :middleware, :autoload_once_paths, :autoload_paths
+ attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
def initialize(root=nil)
super()
@@ -39,16 +39,16 @@ module Rails
@paths ||= begin
paths = Rails::Paths::Root.new(@root)
- paths.add "app", autoload: true, glob: "*"
+ paths.add "app", eager_load: true, glob: "*"
paths.add "app/assets", glob: "*"
- paths.add "app/controllers", autoload: true
- paths.add "app/helpers", autoload: true
- paths.add "app/models", autoload: true
- paths.add "app/mailers", autoload: true
+ paths.add "app/controllers", eager_load: true
+ paths.add "app/helpers", eager_load: true
+ paths.add "app/models", eager_load: true
+ paths.add "app/mailers", eager_load: true
paths.add "app/views"
- paths.add "app/controllers/concerns", autoload: true
- paths.add "app/models/concerns", autoload: true
+ paths.add "app/controllers/concerns", eager_load: true
+ paths.add "app/models/concerns", eager_load: true
paths.add "lib", load_path: true
paths.add "lib/assets", glob: "*"
@@ -76,13 +76,7 @@ module Rails
end
def eager_load_paths
- ActiveSupport::Deprecation.warn "eager_load_paths is deprecated and all autoload_paths are now eagerly loaded."
- autoload_paths
- end
-
- def eager_load_paths=(paths)
- ActiveSupport::Deprecation.warn "eager_load_paths is deprecated and all autoload_paths are now eagerly loaded."
- self.autoload_paths = paths
+ @eager_load_paths ||= paths.eager_load
end
def autoload_once_paths
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index cb3aca5811..28593c5907 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -215,7 +215,7 @@ module Rails
# Make an entry in Rails routing file config/routes.rb
#
- # route "root :to => 'welcome#index'"
+ # route "root 'welcome#index'"
def route(routing_code)
log :route, routing_code
sentinel = /\.routes\.draw do\s*$/
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index c61fc388f0..4e703151ba 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -19,11 +19,8 @@ module Rails
argument :app_path, type: :string
def self.add_shared_options_for(name)
- class_option :builder, type: :string, aliases: '-b',
- desc: "Path to a #{name} builder (can be a filesystem path or URL)"
-
class_option :template, type: :string, aliases: '-m',
- desc: "Path to an #{name} template (can be a filesystem path or URL)"
+ desc: "Path to some #{name} template (can be a filesystem path or URL)"
class_option :skip_gemfile, type: :boolean, default: false,
desc: "Don't create a Gemfile"
@@ -81,17 +78,6 @@ module Rails
def builder
@builder ||= begin
- if path = options[:builder]
- if URI(path).is_a?(URI::HTTP)
- contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read }
- else
- contents = open(File.expand_path(path, @original_wd)) {|io| io.read }
- end
-
- prok = eval("proc { #{contents} }", TOPLEVEL_BINDING, path, 1)
- instance_eval(&prok)
- end
-
builder_class = get_builder_class
builder_class.send(:include, ActionMethods)
builder_class.new(self)
@@ -210,9 +196,8 @@ module Rails
# Gems used only for assets and not required
# in production environments by default.
group :assets do
- gem 'sprockets-rails', '~> 2.0.0.rc1'
- gem 'sass-rails', '~> 4.0.0.beta'
- gem 'coffee-rails', '~> 4.0.0.beta'
+ gem 'sass-rails', '~> 4.0.0.beta1'
+ gem 'coffee-rails', '~> 4.0.0.beta1'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
#{javascript_runtime_gemfile_entry}
diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb
index 5bf98bb6e0..cd69a017dd 100644
--- a/railties/lib/rails/generators/migration.rb
+++ b/railties/lib/rails/generators/migration.rb
@@ -52,7 +52,7 @@ module Rails
if destination && options.force?
remove_file(destination)
elsif destination
- raise Error, "Another migration is already named #{@migration_file_name}: #{destination}"
+ raise Error, "Another migration is already named #{@migration_file_name}: #{destination}. Use --force to remove the old migration file and replace it."
end
destination = File.join(migration_dir, "#{@migration_number}_#{@migration_file_name}.rb")
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index d149413e2e..daf399a538 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -20,9 +20,6 @@ module <%= app_const_base %>
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
- # Custom directories with classes and modules you want to be autoloadable.
- # config.autoload_paths += %W(#{config.root}/extras)
-
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
index 53620dc8e2..7ef89d6608 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
@@ -5,18 +5,18 @@
# gem install activerecord-sqlserver-adapter
#
# Ensure the activerecord adapter and db driver gems are defined in your Gemfile
-# gem 'tiny_tds'
-# gem 'activerecord-sqlserver-adapter'
+# gem 'tiny_tds'
+# gem 'activerecord-sqlserver-adapter'
#
# You should make sure freetds is configured correctly first.
# freetds.conf contains host/port/protocol_versions settings.
# http://freetds.schemamania.org/userguide/freetdsconf.htm
#
# A typical Microsoft server
-# [mssql]
+# [mssql]
# host = mssqlserver.yourdomain.com
-# port = 1433
-# tds version = 7.1
+# port = 1433
+# tds version = 7.1
# If you can connect with "tsql -S servername", your basic FreeTDS installation is working.
# 'man tsql' for more info
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index c4cc1162a4..d0e62d09cc 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -20,10 +20,6 @@
config.active_support.deprecation = :log
<%- unless options.skip_active_record? -%>
- # Log the query plan for queries taking more than this (works
- # with SQLite, MySQL, and PostgreSQL).
- config.active_record.auto_explain_threshold_in_seconds = 0.5
-
# Raise an error on page load if there are pending migrations
config.active_record.migration_error = :page_load
<%- end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 0ab91d9864..5669fe6d64 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -76,12 +76,6 @@
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
- <%- unless options.skip_active_record? -%>
- # Log the query plan for queries taking more than this (works
- # with SQLite, MySQL, and PostgreSQL).
- # config.active_record.auto_explain_threshold_in_seconds = 0.5
- <%- end -%>
-
# Disable automatic flushing of the log to improve performance.
# config.autoflush_log = false
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
index 22a6aeb5fe..f877fa1f8a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -3,7 +3,7 @@
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
- # root to: 'welcome#index'
+ # root 'welcome#index'
# Example of regular route:
# get 'products/:id' => 'catalog#view'
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb b/railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb
index c78bfb7f63..ef360470a3 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb
@@ -1,9 +1,5 @@
-gemfile = File.expand_path('../../../../Gemfile', __FILE__)
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
-if File.exist?(gemfile)
- ENV['BUNDLE_GEMFILE'] = gemfile
- require 'bundler'
- Bundler.setup
-end
-
-$:.unshift File.expand_path('../../../../lib', __FILE__) \ No newline at end of file
+require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
+$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
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 e813437d75..73e89086a5 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -47,7 +47,7 @@ class <%= controller_class_name %>Controller < ApplicationController
# DELETE <%= route_url %>/1
def destroy
@<%= orm_instance.destroy %>
- redirect_to <%= index_helper %>_url
+ redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %>
end
private
@@ -56,7 +56,7 @@ class <%= controller_class_name %>Controller < ApplicationController
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
end
- # Never trust parameters from the scary internet, only allow the white list through.
+ # Only allow a trusted parameter "white list" through.
def <%= "#{singular_table_name}_params" %>
<%- if attributes_names.empty? -%>
params[<%= ":#{singular_table_name}" %>]
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 80ba144441..de6795eda2 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -5,13 +5,13 @@ module Rails
# paths by a Hash like API. It requires you to give a physical path on initialization.
#
# root = Root.new "/rails"
- # root.add "app/controllers", autoload: true
+ # root.add "app/controllers", eager_load: true
#
# The command above creates a new root object and add "app/controllers" as a path.
# This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
#
# path = root["app/controllers"]
- # path.autoload? # => true
+ # path.eager_load? # => true
# path.is_a?(Rails::Paths::Path) # => true
#
# The +Path+ object is simply an enumerable and allows you to easily add extra paths:
@@ -30,7 +30,7 @@ module Rails
# root["config/routes"].inspect # => ["config/routes.rb"]
#
# The +add+ method accepts the following options as arguments:
- # autoload, autoload_once and glob.
+ # eager_load, autoload, autoload_once and glob.
#
# Finally, the +Path+ object also provides a few helpers:
#
@@ -85,8 +85,7 @@ module Rails
end
def eager_load
- ActiveSupport::Deprecation.warn "eager_load is deprecated and all autoload_paths are now eagerly loaded."
- filter_by(:autoload?)
+ filter_by(:eager_load?)
end
def autoload_paths
@@ -125,13 +124,9 @@ module Rails
@glob = options[:glob]
options[:autoload_once] ? autoload_once! : skip_autoload_once!
+ options[:eager_load] ? eager_load! : skip_eager_load!
options[:autoload] ? autoload! : skip_autoload!
options[:load_path] ? load_path! : skip_load_path!
-
- if !options.key?(:autoload) && options.key?(:eager_load)
- ActiveSupport::Deprecation.warn "the :eager_load option is deprecated and all :autoload paths are now eagerly loaded."
- options[:eager_load] ? autoload! : skip_autoload!
- end
end
def children
@@ -148,37 +143,22 @@ module Rails
expanded.last
end
- %w(autoload_once autoload load_path).each do |m|
+ %w(autoload_once eager_load autoload load_path).each do |m|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{m}! # def autoload!
- @#{m} = true # @autoload = true
+ def #{m}! # def eager_load!
+ @#{m} = true # @eager_load = true
end # end
#
- def skip_#{m}! # def skip_autoload!
- @#{m} = false # @autoload = false
+ def skip_#{m}! # def skip_eager_load!
+ @#{m} = false # @eager_load = false
end # end
#
- def #{m}? # def autoload?
- @#{m} # @autoload
+ def #{m}? # def eager_load?
+ @#{m} # @eager_load
end # end
RUBY
end
- def eager_load!
- ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
- autoload!
- end
-
- def skip_eager_load!
- ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
- skip_autoload!
- end
-
- def eager_load?
- ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
- autoload?
- end
-
def each(&block)
@paths.each(&block)
end
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
index 4536fedaa3..3b7f358a5b 100644
--- a/railties/lib/rails/ruby_version_check.rb
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -2,12 +2,12 @@ if RUBY_VERSION < '1.9.3'
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
- Rails 4 requires Ruby 1.9.3+.
+ Rails 4 prefers to run on Ruby 2.0.
You're running
#{desc}
- Please upgrade to continue.
+ Please upgrade to Ruby 1.9.3 or newer to continue.
end_message
end
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index ea6c074bdc..0057b0f887 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -102,7 +102,7 @@ namespace :doc do
# desc "Generate Rails Guides"
task :guides do
# FIXME: Reaching outside lib directory is a bad idea
- require File.expand_path('../../../../guides/rails_guides', __FILE__)
+ require File.expand_path('../../../../../guides/rails_guides', __FILE__)
RailsGuides::Generator.new(Rails.root.join("doc/guides")).generate
end
end
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index ec878f9dcf..87fc7690ac 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -3,7 +3,7 @@ module Rails
MAJOR = 4
MINOR = 0
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/railties/test/app_rails_loader_test.rb b/railties/test/app_rails_loader_test.rb
index 87e0ad7bd7..63ed9eaef0 100644
--- a/railties/test/app_rails_loader_test.rb
+++ b/railties/test/app_rails_loader_test.rb
@@ -2,40 +2,47 @@ require 'abstract_unit'
require 'rails/app_rails_loader'
class AppRailsLoaderTest < ActiveSupport::TestCase
- test "is in a rails application if bin/rails exists and contains APP_PATH" do
- File.stubs(:exists?).returns(true)
- File.stubs(:read).with('bin/rails').returns('APP_PATH')
- assert Rails::AppRailsLoader.in_rails_application_or_engine?
- end
- test "is not in a rails application if bin/rails exists but doesn't contain APP_PATH" do
- File.stubs(:exists?).returns(true)
- File.stubs(:read).with('bin/rails').returns('railties bin/rails')
- assert !Rails::AppRailsLoader.in_rails_application_or_engine?
+ setup do
+ File.stubs(:exists?).returns(false)
end
- test "is in a rails application if parent directory has bin/rails containing APP_PATH" do
- File.stubs(:exists?).with("/foo/bar/bin/rails").returns(false)
- File.stubs(:exists?).with("/foo/bin/rails").returns(true)
- File.stubs(:read).with('/foo/bin/rails').returns('APP_PATH')
- assert Rails::AppRailsLoader.in_rails_application_or_engine_subdirectory?(Pathname.new("/foo/bar"))
- end
+ ['bin/rails', 'script/rails'].each do |exe|
+ test "is in a rails application if #{exe} exists and contains APP_PATH" do
+ File.stubs(:exists?).with(exe).returns(true)
+ File.stubs(:read).with(exe).returns('APP_PATH')
+ assert Rails::AppRailsLoader.find_executable
+ end
- test "is not in a rails application if at the root directory and doesn't have bin/rails" do
- Pathname.any_instance.stubs(:root?).returns true
- assert !Rails::AppRailsLoader.in_rails_application_or_engine?
- end
+ test "is not in a rails application if #{exe} exists but doesn't contain APP_PATH" do
+ File.stubs(:exists?).with(exe).returns(true)
+ File.stubs(:read).with(exe).returns("railties #{exe}")
+ assert !Rails::AppRailsLoader.find_executable
+ end
- test "is in a rails engine if parent directory has bin/rails containing ENGINE_PATH" do
- File.stubs(:exists?).with("/foo/bar/bin/rails").returns(false)
- File.stubs(:exists?).with("/foo/bin/rails").returns(true)
- File.stubs(:read).with('/foo/bin/rails').returns('ENGINE_PATH')
- assert Rails::AppRailsLoader.in_rails_application_or_engine_subdirectory?(Pathname.new("/foo/bar"))
- end
+ test "is in a rails application if parent directory has #{exe} containing APP_PATH" do
+ File.stubs(:exists?).with("/foo/bar/#{exe}").returns(false)
+ File.stubs(:exists?).with("/foo/#{exe}").returns(true)
+ File.stubs(:read).with("/foo/#{exe}").returns('APP_PATH')
+ assert Rails::AppRailsLoader.find_executable_in_parent_path(Pathname.new("/foo/bar"))
+ end
+
+ test "is not in a rails application if at the root directory and doesn't have #{exe}" do
+ Pathname.any_instance.stubs(:root?).returns true
+ assert !Rails::AppRailsLoader.find_executable
+ end
+
+ test "is in a rails engine if parent directory has #{exe} containing ENGINE_PATH" do
+ File.stubs(:exists?).with("/foo/bar/#{exe}").returns(false)
+ File.stubs(:exists?).with("/foo/#{exe}").returns(true)
+ File.stubs(:read).with("/foo/#{exe}").returns('ENGINE_PATH')
+ assert Rails::AppRailsLoader.find_executable_in_parent_path(Pathname.new("/foo/bar"))
+ end
- test "is in a rails engine if bin/rails exists containing ENGINE_PATH" do
- File.stubs(:exists?).returns(true)
- File.stubs(:read).with('bin/rails').returns('ENGINE_PATH')
- assert Rails::AppRailsLoader.in_rails_application_or_engine?
+ test "is in a rails engine if #{exe} exists containing ENGINE_PATH" do
+ File.stubs(:exists?).with(exe).returns(true)
+ File.stubs(:read).with(exe).returns('ENGINE_PATH')
+ assert Rails::AppRailsLoader.find_executable
+ end
end
end
diff --git a/railties/test/application/initializers/boot_test.rb b/railties/test/application/initializers/boot_test.rb
deleted file mode 100644
index 7eda15894e..0000000000
--- a/railties/test/application/initializers/boot_test.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require "isolation/abstract_unit"
-
-module ApplicationTests
- class BootTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::Isolation
-
- def setup
- # build_app
- # boot_rails
- end
-
- def teardown
- # teardown_app
- end
-
- test "booting rails sets the load paths correctly" do
- # This test is pending reworking the boot process
- end
- end
-end
diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb
index 595f58bd91..31811e7f92 100644
--- a/railties/test/application/initializers/load_path_test.rb
+++ b/railties/test/application/initializers/load_path_test.rb
@@ -64,7 +64,7 @@ module ApplicationTests
add_to_config <<-RUBY
config.root = "#{app_path}"
- config.autoload_paths << "#{app_path}/lib"
+ config.eager_load_paths << "#{app_path}/lib"
RUBY
require "#{app_path}/config/environment"
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
index f6a77a0d17..4029984ce9 100644
--- a/railties/test/application/paths_test.rb
+++ b/railties/test/application/paths_test.rb
@@ -54,11 +54,11 @@ module ApplicationTests
assert_equal root("app", "controllers"), @paths["app/controllers"].expanded.first
end
- test "booting up Rails yields a list of paths that will be eager loaded" do
- autoload_paths = @paths.autoload_paths
- assert autoload_paths.include?(root("app/controllers"))
- assert autoload_paths.include?(root("app/helpers"))
- assert autoload_paths.include?(root("app/models"))
+ test "booting up Rails yields a list of paths that are eager" do
+ eager_load = @paths.eager_load
+ assert eager_load.include?(root("app/controllers"))
+ assert eager_load.include?(root("app/helpers"))
+ assert eager_load.include?(root("app/models"))
end
test "environments has a glob equal to the current environment" do
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index ccb47663d4..820b838702 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -21,6 +21,8 @@ module ApplicationTests
def set_database_url
ENV['DATABASE_URL'] = "sqlite3://:@localhost/#{database_url_db_name}"
+ # ensure it's using the DATABASE_URL
+ FileUtils.rm_rf("#{app_path}/config/database.yml")
end
def expected
@@ -166,9 +168,16 @@ module ApplicationTests
end
test 'db:test:load_structure with database_url' do
- require "#{app_path}/config/environment"
- set_database_url
- db_test_load_structure
+ old_rails_env = ENV["RAILS_ENV"]
+ ENV["RAILS_ENV"] = 'test'
+
+ begin
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_test_load_structure
+ ensure
+ ENV["RAILS_ENV"] = old_rails_env
+ end
end
end
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index a8275a2e76..09f2ad1209 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -1,5 +1,6 @@
# coding:utf-8
require "isolation/abstract_unit"
+require "active_support/core_ext/string/strip"
module ApplicationTests
class RakeTest < ActiveSupport::TestCase
@@ -140,7 +141,24 @@ module ApplicationTests
get '/cart', to: 'cart#show'
end
RUBY
- assert_equal "cart GET /cart(.:format) cart#show\n", Dir.chdir(app_path){ `rake routes` }
+
+ output = Dir.chdir(app_path){ `rake routes` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\ncart GET /cart(.:format) cart#show\n", output
+ end
+
+ def test_rake_routes_displays_message_when_no_routes_are_defined
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ end
+ RUBY
+
+ assert_equal <<-MESSAGE.strip_heredoc, Dir.chdir(app_path){ `rake routes` }
+ You don't have any routes defined!
+
+ Please add some routes in config/routes.rb.
+
+ For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
+ MESSAGE
end
def test_logger_is_flushed_when_exiting_production_rake_tasks
diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb
new file mode 100644
index 0000000000..b3eabf5024
--- /dev/null
+++ b/railties/test/code_statistics_calculator_test.rb
@@ -0,0 +1,288 @@
+require 'abstract_unit'
+require 'rails/code_statistics_calculator'
+
+class CodeStatisticsCalculatorTest < ActiveSupport::TestCase
+ def setup
+ @code_statistics_calculator = CodeStatisticsCalculator.new
+ end
+
+ test 'add statistics to another using #add' do
+ code_statistics_calculator_1 = CodeStatisticsCalculator.new(1, 2, 3, 4)
+ @code_statistics_calculator.add(code_statistics_calculator_1)
+
+ assert_equal 1, @code_statistics_calculator.lines
+ assert_equal 2, @code_statistics_calculator.code_lines
+ assert_equal 3, @code_statistics_calculator.classes
+ assert_equal 4, @code_statistics_calculator.methods
+
+ code_statistics_calculator_2 = CodeStatisticsCalculator.new(2, 3, 4, 5)
+ @code_statistics_calculator.add(code_statistics_calculator_2)
+
+ assert_equal 3, @code_statistics_calculator.lines
+ assert_equal 5, @code_statistics_calculator.code_lines
+ assert_equal 7, @code_statistics_calculator.classes
+ assert_equal 9, @code_statistics_calculator.methods
+ end
+
+ test 'accumulate statistics using #add_by_io' do
+ code_statistics_calculator_1 = CodeStatisticsCalculator.new(1, 2, 3, 4)
+ @code_statistics_calculator.add(code_statistics_calculator_1)
+
+ code = <<-'CODE'
+ def foo
+ puts 'foo'
+ end
+
+ def bar; end
+ class A; end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 7, @code_statistics_calculator.lines
+ assert_equal 7, @code_statistics_calculator.code_lines
+ assert_equal 4, @code_statistics_calculator.classes
+ assert_equal 6, @code_statistics_calculator.methods
+ end
+
+ test 'calculate statistics using #add_by_file_path' do
+ tmp_path = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'tmp'))
+ FileUtils.mkdir_p(tmp_path)
+
+ code = <<-'CODE'
+ def foo
+ puts 'foo'
+ # bar
+ end
+ CODE
+
+ file_path = "#{tmp_path}/stats.rb"
+ File.open(file_path, 'w') { |f| f.write(code) }
+
+ @code_statistics_calculator.add_by_file_path(file_path)
+
+ assert_equal 4, @code_statistics_calculator.lines
+ assert_equal 3, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 1, @code_statistics_calculator.methods
+
+ FileUtils.rm_rf(tmp_path)
+ end
+
+ test 'calculate number of Ruby methods' do
+ code = <<-'CODE'
+ def foo
+ puts 'foo'
+ end
+
+ def bar; end
+
+ class Foo
+ def bar(abc)
+ end
+ end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 3, @code_statistics_calculator.methods
+ end
+
+ test 'calculate Ruby LOCs' do
+ code = <<-'CODE'
+ def foo
+ puts 'foo'
+ end
+
+ # def bar; end
+
+ class A < B
+ end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 8, @code_statistics_calculator.lines
+ assert_equal 5, @code_statistics_calculator.code_lines
+ end
+
+ test 'calculate number of Ruby classes' do
+ code = <<-'CODE'
+ class Foo < Bar
+ def foo
+ puts 'foo'
+ end
+ end
+
+ class Z; end
+
+ # class A
+ # end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 2, @code_statistics_calculator.classes
+ end
+
+ test 'skip Ruby comments' do
+ code = <<-'CODE'
+=begin
+ class Foo
+ def foo
+ puts 'foo'
+ end
+ end
+=end
+
+ # class A
+ # end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 10, @code_statistics_calculator.lines
+ assert_equal 0, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 0, @code_statistics_calculator.methods
+ end
+
+ test 'calculate number of JS methods' do
+ code = <<-'CODE'
+ function foo(x, y, z) {
+ doX();
+ }
+
+ $(function () {
+ bar();
+ })
+
+ var baz = function ( x ) {
+ }
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :js)
+
+ assert_equal 3, @code_statistics_calculator.methods
+ end
+
+ test 'calculate JS LOCs' do
+ code = <<-'CODE'
+ function foo()
+ alert('foo');
+ end
+
+ // var b = 2;
+
+ var a = 1;
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :js)
+
+ assert_equal 7, @code_statistics_calculator.lines
+ assert_equal 4, @code_statistics_calculator.code_lines
+ end
+
+ test 'skip JS comments' do
+ code = <<-'CODE'
+ /*
+ * var f = function () {
+ 1 / 2;
+ }
+ */
+
+ // call();
+ //
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :js)
+
+ assert_equal 8, @code_statistics_calculator.lines
+ assert_equal 0, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 0, @code_statistics_calculator.methods
+ end
+
+ test 'calculate number of CoffeeScript methods' do
+ code = <<-'CODE'
+ square = (x) -> x * x
+
+ math =
+ cube: (x) -> x * square x
+
+ fill = (container, liquid = "coffee") ->
+ "Filling the #{container} with #{liquid}..."
+
+ $('.shopping_cart').bind 'click', (event) =>
+ @customer.purchase @cart
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 4, @code_statistics_calculator.methods
+ end
+
+ test 'calculate CoffeeScript LOCs' do
+ code = <<-'CODE'
+ # Assignment:
+ number = 42
+ opposite = true
+
+ ###
+ CoffeeScript Compiler v1.4.0
+ Released under the MIT License
+ ###
+
+ # Conditions:
+ number = -42 if opposite
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 11, @code_statistics_calculator.lines
+ assert_equal 3, @code_statistics_calculator.code_lines
+ end
+
+ test 'calculate number of CoffeeScript classes' do
+ code = <<-'CODE'
+ class Animal
+ constructor: (@name) ->
+
+ move: (meters) ->
+ alert @name + " moved #{meters}m."
+
+ class Snake extends Animal
+ move: ->
+ alert "Slithering..."
+ super 5
+
+ # class Horse
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 2, @code_statistics_calculator.classes
+ end
+
+ test 'skip CoffeeScript comments' do
+ code = <<-'CODE'
+###
+class Animal
+ constructor: (@name) ->
+
+ move: (meters) ->
+ alert @name + " moved #{meters}m."
+ ###
+
+ # class Horse
+ alert 'hello'
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 10, @code_statistics_calculator.lines
+ assert_equal 1, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 0, @code_statistics_calculator.methods
+ end
+end
diff --git a/railties/test/fixtures/lib/app_builders/empty_builder.rb b/railties/test/fixtures/lib/app_builders/empty_builder.rb
deleted file mode 100644
index babd9c2461..0000000000
--- a/railties/test/fixtures/lib/app_builders/empty_builder.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class AppBuilder
-end \ No newline at end of file
diff --git a/railties/test/fixtures/lib/app_builders/simple_builder.rb b/railties/test/fixtures/lib/app_builders/simple_builder.rb
deleted file mode 100644
index 993d3a2aa2..0000000000
--- a/railties/test/fixtures/lib/app_builders/simple_builder.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class AppBuilder
- def gitignore
- create_file ".gitignore", <<-R.strip
-foobar
- R
- end
-end
diff --git a/railties/test/fixtures/lib/app_builders/tweak_builder.rb b/railties/test/fixtures/lib/app_builders/tweak_builder.rb
deleted file mode 100644
index cb50be01cb..0000000000
--- a/railties/test/fixtures/lib/app_builders/tweak_builder.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class AppBuilder < Rails::AppBuilder
- def gitignore
- create_file ".gitignore", <<-R.strip
-foobar
- R
- end
-end
diff --git a/railties/test/fixtures/lib/plugin_builders/empty_builder.rb b/railties/test/fixtures/lib/plugin_builders/empty_builder.rb
deleted file mode 100644
index 5c5607621c..0000000000
--- a/railties/test/fixtures/lib/plugin_builders/empty_builder.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class PluginBuilder
-end
diff --git a/railties/test/fixtures/lib/plugin_builders/simple_builder.rb b/railties/test/fixtures/lib/plugin_builders/simple_builder.rb
deleted file mode 100644
index 08f6c5535d..0000000000
--- a/railties/test/fixtures/lib/plugin_builders/simple_builder.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class PluginBuilder
- def gitignore
- create_file ".gitignore", <<-R.strip
-foobar
- R
- end
-end
diff --git a/railties/test/fixtures/lib/plugin_builders/spec_builder.rb b/railties/test/fixtures/lib/plugin_builders/spec_builder.rb
deleted file mode 100644
index 2721429599..0000000000
--- a/railties/test/fixtures/lib/plugin_builders/spec_builder.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class PluginBuilder < Rails::PluginBuilder
- def test
- create_file "spec/spec_helper.rb"
- append_file "Rakefile", <<-EOF
-# spec tasks in rakefile
-
-task default: :spec
- EOF
- end
-
- def generate_test_dummy
- dummy_path("spec/dummy")
- super
- end
-
- def skip_test_unit?
- true
- end
-end
diff --git a/railties/test/fixtures/lib/plugin_builders/tweak_builder.rb b/railties/test/fixtures/lib/plugin_builders/tweak_builder.rb
deleted file mode 100644
index 1e801409a4..0000000000
--- a/railties/test/fixtures/lib/plugin_builders/tweak_builder.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class PluginBuilder < Rails::PluginBuilder
- def gitignore
- create_file ".gitignore", <<-R.strip
-foobar
- R
- end
-end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 83f43d1025..0697035871 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -342,15 +342,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_generated_environments_file_for_auto_explain
- run_generator [destination_root, "--skip-active-record"]
- %w(development production).each do |env|
- assert_file "config/environments/#{env}.rb" do |file|
- assert_no_match %r(auto_explain_threshold_in_seconds), file
- end
- end
- end
-
def test_pretend_option
output = run_generator [File.join(destination_root, "myapp"), "--pretend"]
assert_no_match(/run bundle install/, output)
@@ -366,28 +357,3 @@ protected
assert_file "Gemfile", /^gem\s+["']#{gem}["']$/
end
end
-
-class CustomAppGeneratorTest < Rails::Generators::TestCase
- include GeneratorsTestHelper
- tests Rails::Generators::AppGenerator
-
- arguments [destination_root]
- include SharedCustomGeneratorTests
-
-protected
- def default_files
- ::DEFAULT_APP_FILES
- end
-
- def builders_dir
- "app_builders"
- end
-
- def builder_class
- :AppBuilder
- end
-
- def action(*args, &block)
- silence(:stdout) { generator.send(*args, &block) }
- end
-end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 4bf5a2f08b..904c1db57e 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -371,38 +371,3 @@ protected
::DEFAULT_PLUGIN_FILES
end
end
-
-class CustomPluginGeneratorTest < Rails::Generators::TestCase
- include GeneratorsTestHelper
- tests Rails::Generators::PluginNewGenerator
-
- destination File.join(Rails.root, "tmp/bukkits")
- arguments [destination_root]
- include SharedCustomGeneratorTests
-
- def test_overriding_test_framework
- FileUtils.cd(destination_root)
- run_generator([destination_root, "-b", "#{Rails.root}/lib/plugin_builders/spec_builder.rb"])
- assert_file 'spec/spec_helper.rb'
- assert_file 'spec/dummy'
- assert_file 'Rakefile', /task default: :spec/
- assert_file 'Rakefile', /# spec tasks in rakefile/
- end
-
-protected
- def default_files
- ::DEFAULT_PLUGIN_FILES
- end
-
- def builder_class
- :PluginBuilder
- end
-
- def builders_dir
- "plugin_builders"
- end
-
- def action(*args, &block)
- silence(:stdout){ generator.send(*args, &block) }
- end
-end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index c34ce285e3..013cb78252 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -39,6 +39,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
assert_instance_method :destroy, content do |m|
assert_match(/@user\.destroy/, m)
+ assert_match(/User was successfully destroyed/, m)
end
assert_instance_method :set_user, content do |m|
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index d203afed5c..2724882a23 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -83,7 +83,7 @@ module SharedGeneratorTests
end
def test_template_is_executed_when_supplied
- path = "http://gist.github.com/103208.txt"
+ path = "https://gist.github.com/josevalim/103208/raw/"
template = %{ say "It works!" }
template.instance_eval "def read; self; end" # Make the string respond to read
@@ -92,7 +92,7 @@ module SharedGeneratorTests
end
def test_template_is_executed_when_supplied_an_https_path
- path = "https://gist.github.com/103208.txt"
+ path = "https://gist.github.com/josevalim/103208/raw/"
template = %{ say "It works!" }
template.instance_eval "def read; self; end" # Make the string respond to read
@@ -140,61 +140,3 @@ module SharedGeneratorTests
assert_no_file('app/mailers/.keep')
end
end
-
-module SharedCustomGeneratorTests
- def setup
- Rails.application = TestApp::Application
- super
- Rails::Generators::AppGenerator.instance_variable_set('@desc', nil)
- end
-
- def teardown
- super
- Rails::Generators::AppGenerator.instance_variable_set('@desc', nil)
- Object.class_eval do
- remove_const :AppBuilder if const_defined?(:AppBuilder)
- remove_const :PluginBuilder if const_defined?(:PluginBuilder)
- end
- Rails.application = TestApp::Application.instance
- end
-
- def test_builder_option_with_empty_app_builder
- FileUtils.cd(destination_root)
- run_generator([destination_root, "-b", "#{Rails.root}/lib/#{builders_dir}/empty_builder.rb"])
- default_files.each{ |path| assert_no_file path }
- end
-
- def test_builder_option_with_simple_plugin_builder
- FileUtils.cd(destination_root)
- run_generator([destination_root, "-b", "#{Rails.root}/lib/#{builders_dir}/simple_builder.rb"])
- (default_files - ['.gitignore']).each{ |path| assert_no_file path }
- assert_file ".gitignore", "foobar"
- end
-
- def test_builder_option_with_relative_path
- here = File.expand_path(File.dirname(__FILE__))
- FileUtils.cd(here)
- run_generator([destination_root, "-b", "../fixtures/lib/#{builders_dir}/simple_builder.rb"])
- FileUtils.cd(destination_root)
- (default_files - ['.gitignore']).each{ |path| assert_no_file path }
- assert_file ".gitignore", "foobar"
- end
-
- def test_builder_option_with_tweak_plugin_builder
- FileUtils.cd(destination_root)
- run_generator([destination_root, "-b", "#{Rails.root}/lib/#{builders_dir}/tweak_builder.rb"])
- default_files.each{ |path| assert_file path }
- assert_file ".gitignore", "foobar"
- end
-
- def test_builder_option_with_http
- url = "http://gist.github.com/103208.txt"
- template = "class #{builder_class}; end"
- template.instance_eval "def read; self; end" # Make the string respond to read
-
- generator([destination_root], builder: url).expects(:open).with(url, 'Accept' => 'application/x-thor-template').returns(template)
- quietly { generator.invoke_all }
-
- default_files.each{ |path| assert_no_file(path) }
- end
-end
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index 6f860469fd..12f18b9dbf 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -135,48 +135,56 @@ class PathsTest < ActiveSupport::TestCase
assert_equal 2, @root.autoload_once.size
end
- test "marking a path as eager loaded is deprecated" do
+ test "it is possible to mark a path as eager loaded" do
@root["app"] = "/app"
- assert_deprecated{ @root["app"].eager_load! }
- assert_deprecated{ assert @root["app"].eager_load? }
- assert_deprecated{ assert @root.eager_load.include?(@root["app"].to_a.first) }
+ @root["app"].eager_load!
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?(@root["app"].to_a.first)
end
- test "skipping a path from eager loading is deprecated" do
+ test "it is possible to skip a path from eager loading" do
@root["app"] = "/app"
- assert_deprecated{ @root["app"].eager_load! }
- assert_deprecated{ assert @root["app"].eager_load? }
+ @root["app"].eager_load!
+ assert @root["app"].eager_load?
- assert_deprecated{ @root["app"].skip_eager_load! }
- assert_deprecated{ assert !@root["app"].eager_load? }
- assert_deprecated{ assert !@root.eager_load.include?(@root["app"].to_a.first) }
+ @root["app"].skip_eager_load!
+ assert !@root["app"].eager_load?
+ assert !@root.eager_load.include?(@root["app"].to_a.first)
end
- test "adding a path with eager_load option is deprecated" do
- assert_deprecated{ @root.add "app", with: "/app", eager_load: true }
- assert_deprecated{ assert @root["app"].eager_load? }
- assert_deprecated{ assert @root.eager_load.include?("/app") }
+ test "it is possible to add a path without assignment and mark it as eager" do
+ @root.add "app", with: "/app", eager_load: true
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?("/app")
end
- test "adding multiple paths with eager_load option is deprecated" do
- assert_deprecated{ @root.add "app", with: ["/app", "/app2"], eager_load: true }
- assert_deprecated{ assert @root["app"].eager_load? }
- assert_deprecated{ assert @root.eager_load.include?("/app") }
- assert_deprecated{ assert @root.eager_load.include?("/app2") }
+ test "it is possible to add multiple paths without assignment and mark them as eager" do
+ @root.add "app", with: ["/app", "/app2"], eager_load: true
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?("/app")
+ assert @root.eager_load.include?("/app2")
+ end
+
+ test "it is possible to create a path without assignment and mark it both as eager and load once" do
+ @root.add "app", with: "/app", eager_load: true, autoload_once: true
+ assert @root["app"].eager_load?
+ assert @root["app"].autoload_once?
+ assert @root.eager_load.include?("/app")
+ assert @root.autoload_once.include?("/app")
end
test "making a path eager more than once only includes it once in @root.eager_paths" do
@root["app"] = "/app"
- assert_deprecated{ @root["app"].eager_load! }
- assert_deprecated{ @root["app"].eager_load! }
- assert_deprecated{ assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size }
+ @root["app"].eager_load!
+ @root["app"].eager_load!
+ assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size
end
test "paths added to a eager_load path should be added to the eager_load collection" do
@root["app"] = "/app"
- assert_deprecated{ @root["app"].eager_load! }
+ @root["app"].eager_load!
@root["app"] << "/app2"
- assert_deprecated{ assert_equal 2, @root.eager_load.size }
+ assert_equal 2, @root.eager_load.size
end
test "it should be possible to add a path's default glob" do
diff --git a/version.rb b/version.rb
index ec878f9dcf..87fc7690ac 100644
--- a/version.rb
+++ b/version.rb
@@ -3,7 +3,7 @@ module Rails
MAJOR = 4
MINOR = 0
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end