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.md88
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb14
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb35
-rw-r--r--actionpack/lib/action_dispatch/journey/path/pattern.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb75
-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.rb4
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionpack/test/activerecord/controller_runtime_test.rb14
-rw-r--r--actionpack/test/controller/mime_responds_test.rb21
-rw-r--r--actionpack/test/controller/routing_test.rb16
-rw-r--r--actionpack/test/dispatch/routing_test.rb109
-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.rb2
-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/number_helper_test.rb348
-rw-r--r--activemodel/CHANGELOG.md53
-rw-r--r--activemodel/lib/active_model/version.rb2
-rw-r--r--activerecord/CHANGELOG.md104
-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.rb4
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb6
-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.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb6
-rw-r--r--activerecord/lib/active_record/core.rb1
-rw-r--r--activerecord/lib/active_record/explain.rb53
-rw-r--r--activerecord/lib/active_record/persistence.rb24
-rw-r--r--activerecord/lib/active_record/querying.rb24
-rw-r--r--activerecord/lib/active_record/railtie.rb22
-rw-r--r--activerecord/lib/active_record/railties/controller_runtime.rb3
-rw-r--r--activerecord/lib/active_record/relation.rb12
-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.rb5
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb25
-rw-r--r--activerecord/lib/active_record/transactions.rb31
-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/hstore_test.rb15
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb15
-rw-r--r--activerecord/test/cases/associations/eager_test.rb12
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb10
-rw-r--r--activerecord/test/cases/base_test.rb7
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb21
-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)30
-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/schema_dumper_test.rb12
-rw-r--r--activerecord/test/cases/timestamp_test.rb12
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb35
-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.md171
-rw-r--r--activesupport/README.rdoc4
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support/cache/file_store.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/number_helper.rb2
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb4
-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/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.md2
-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_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/layouts_and_rendering.md3
-rw-r--r--guides/source/routing.md12
-rw-r--r--guides/source/upgrading_ruby_on_rails.md14
-rw-r--r--install.rb6
-rw-r--r--rails.gemspec4
-rw-r--r--railties/CHANGELOG.md21
-rw-r--r--railties/lib/rails/app_rails_loader.rb24
-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.rb3
-rw-r--r--railties/lib/rails/generators/actions.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb19
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb4
-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.tt12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb12
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec3
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/Gemfile8
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb12
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/rails/javascripts.js13
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/rails/stylesheets.css13
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb2
-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/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.rb90
-rw-r--r--railties/test/generators/shared_generator_tests.rb58
-rw-r--r--version.rb2
151 files changed, 1845 insertions, 1329 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 5f68fbc608..c45e14abf7 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,60 @@
## Rails 4.0.0 (unreleased) ##
+* `format: true` does not override existing format constraints.
+ Fixes #9466.
+
+ Example:
+
+ # This will force the .json extension.
+ get '/json_only', to: ok, format: true, constraints: { format: /json/ }
+
+ *Yves Senn*
+
+* Skip valid encoding checks for non-String parameters that come
+ from the matched route's defaults.
+ Fixes #9435.
+
+ Example:
+
+ 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.
@@ -279,7 +334,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/'
@@ -378,23 +433,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.
@@ -447,10 +496,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.
@@ -536,9 +581,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*
@@ -812,9 +855,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
@@ -841,7 +884,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*
@@ -871,7 +914,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.
@@ -966,9 +1009,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*
@@ -996,6 +1036,8 @@
not submitted with the form. This is a behavior change, previously the hidden
tag had a value of the disabled checkbox. *Tadas Tamosauskas*
+* `favicon_link_tag` helper will now use the favicon in app/assets by default. *Lucas Caton*
+
* `ActionView::Helpers::TextHelper#highlight` now defaults to the
HTML5 `mark` element. *Brian Cardarella*
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 4b984d0558..f8e4cb4384 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -97,15 +97,23 @@ module AbstractController
self.response_body = render_to_body(options)
end
- # Raw rendering of a template to a string. Just convert the results of
- # render_response into a String.
+ # Raw rendering of a template to a string.
+ #
+ # It is similar to render, except that it does not
+ # set the response_body and it should be guaranteed
+ # to always return a string.
+ #
+ # If a component extends the semantics of response_body
+ # (as Action Controller extends it to be anything that
+ # responds to the method each), this method needs to
+ # overriden in order to still return a string.
# :api: plugin
def render_to_string(*args, &block)
options = _normalize_render(*args, &block)
render_to_body(options)
end
- # Raw rendering of a template to a Rack-compatible body.
+ # Raw rendering of a template.
# :api: plugin
def render_to_body(options = {})
_process_options(options)
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 17379cf7ac..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)
@@ -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_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 ee29e5c59c..36a0db6e61 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -205,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
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 6de9be63c5..93a2b52996 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -9,7 +9,7 @@ module ActionDispatch
# 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.
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 0a41ed0fcf..4fbe156a72 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,44 +110,18 @@ 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
- end
-
- def normalize_format!
- if options[:format] == true
- options[:format] = /.+/
- elsif options[:format] == false
- options.delete(:format)
- end
+ @options.merge!(default_controller_and_action)
end
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
if options[:format] == true
- @requirements[:format] = /.+/
+ @requirements[:format] ||= /.+/
elsif Regexp === options[:format]
@requirements[:format] = options[:format]
elsif String === options[:format]
@@ -156,6 +129,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 +197,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 +210,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
@@ -340,7 +323,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 +419,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 +1369,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 +1384,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 +1424,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 bf78c00e4d..31e37893c6 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -152,7 +152,7 @@ module ActionView
# * <tt>:type</tt> - Override the auto-generated mime type, defaults to 'image/vnd.microsoft.icon'
#
# favicon_link_tag '/myicon.ico'
- # # => <link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
+ # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
# Mobile Safari looks for a different <link> tag, pointing to an image that
# will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
@@ -161,7 +161,7 @@ module ActionView
# favicon_link_tag '/mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
#
- def favicon_link_tag(source='/favicon.ico', options={})
+ def favicon_link_tag(source='favicon.ico', options={})
tag('link', {
:rel => 'shortcut icon',
:type => 'image/vnd.microsoft.icon',
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index a989966613..d3953c26b7 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -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_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/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb
index 1df826fe2b..368bec1c70 100644
--- a/actionpack/test/activerecord/controller_runtime_test.rb
+++ b/actionpack/test/activerecord/controller_runtime_test.rb
@@ -8,6 +8,8 @@ ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime
class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
class LogSubscriberController < ActionController::Base
+ respond_to :html
+
def show
render :inline => "<%= Project.all %>"
end
@@ -16,6 +18,12 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
render :inline => "Zero DB runtime"
end
+ def create
+ ActiveRecord::LogSubscriber.runtime += 100
+ project = Project.last
+ respond_with(project, location: url_for(action: :show))
+ end
+
def redirect
Project.all
redirect_to :action => 'show'
@@ -64,6 +72,12 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
assert_match(/\(Views: [\d.]+ms \| ActiveRecord: 0.0ms\)/, @logger.logged(:info)[1])
end
+ def test_log_with_active_record_when_post
+ post :create
+ wait
+ assert_match(/ActiveRecord: ([1-9][\d.]+)ms\)/, @logger.logged(:info)[2])
+ end
+
def test_log_with_active_record_when_redirecting
get :redirect
wait
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/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/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 143733254b..89f406a3d0 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1146,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
@@ -1319,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' }
@@ -1339,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
@@ -3353,6 +3400,66 @@ 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/ }
+ get '/json_only', to: ok, format: true, constraints: { format: /json/ }
+ get '/xml_only', to: ok, format: 'xml'
+ 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
+
+ def test_enforce_with_format_true_with_constraint
+ get 'http://www.example.com/json_only.json'
+ assert_response :success
+
+ get 'http://www.example.com/json_only.html'
+ assert_response :not_found
+
+ get 'http://www.example.com/json_only'
+ assert_response :not_found
+ end
+
+ def test_enforce_with_string
+ get 'http://www.example.com/xml_only.xml'
+ assert_response :success
+
+ get 'http://www.example.com/xml_only'
+ assert_response :success
+
+ get 'http://www.example.com/xml_only.json'
+ 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 185f742c7f..11614a45dc 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -200,7 +200,7 @@ class AssetTagHelperTest < ActionView::TestCase
}
FaviconLinkToTag = {
- %(favicon_link_tag) => %(<link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
+ %(favicon_link_tag) => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico') => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico', :rel => 'foo') => %(<link href="/images/favicon.ico" rel="foo" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico', :rel => 'foo', :type => 'bar') => %(<link href="/images/favicon.ico" rel="foo" type="bar" />),
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/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/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/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 3410f38063..a5d74418c4 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,92 @@
## Rails 4.0.0 (unreleased) ##
+* Support PostgreSQL specific column types when using `change_table`.
+ Fixes #9480.
+
+ Example:
+
+ change_table :authors do |t|
+ t.hstore :books
+ t.json :metadata
+ end
+
+ *Yves Senn*
+
+* Revert 408227d9c5ed7d, 'quote numeric'. This introduced some regressions.
+
+ *Steve Klabnik*
+
+* Fix calculation of `db_runtime` property in
+ `ActiveRecord::Railties::ControllerRuntime#cleanup_view_runtime`.
+ 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.
@@ -143,8 +230,8 @@
*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:
@@ -153,7 +240,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:
@@ -1214,12 +1301,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*
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 b0b1c13b0d..c4b50ab306 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -47,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/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/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index aec4654eee..d18b9c991f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -25,19 +25,13 @@ 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 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 BigDecimal then value.to_s('F')
+ when Numeric, ActiveSupport::Duration then value.to_s
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 f758e19a4f..42206de8fc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -44,8 +44,8 @@ module ActiveRecord
# Represents the schema of an SQL table in an abstract way. This class
# provides methods for manipulating the schema representation.
#
- # Inside migration files, the +t+ object in +create_table+ and
- # +change_table+ is actually of this type:
+ # Inside migration files, the +t+ object in +create_table+
+ # is actually of this type:
#
# class SomeMigration < ActiveRecord::Migration
# def up
@@ -489,7 +489,7 @@ module ActiveRecord
args.each do |name|
@base.add_column(@table_name, name, column_type, options)
end
- 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 9bae880024..0cce8c7596 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -156,7 +156,7 @@ module ActiveRecord
#
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, options = {})
- td = table_definition
+ td = create_table_definition
td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
yield td if block_given?
@@ -298,10 +298,10 @@ module ActiveRecord
def change_table(table_name, options = {})
if supports_bulk_alter? && options[:bulk]
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
- yield Table.new(table_name, recorder)
+ yield update_table_definition(table_name, recorder)
bulk_change_table(table_name, recorder.commands)
else
- yield Table.new(table_name, self)
+ yield update_table_definition(table_name, self)
end
end
@@ -727,9 +727,13 @@ module ActiveRecord
end
private
- def table_definition
+ def create_table_definition
TableDefinition.new(self)
end
+
+ def update_table_definition(table_name, base)
+ Table.new(table_name, base)
+ end
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 67d4d505f7..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()
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 0871454cfe..9826b18053 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,6 +212,8 @@ 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
@@ -581,7 +583,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 +724,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
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index de418eab14..2c683fc3ac 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -65,10 +65,6 @@ 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 self.connection_url_to_hash(url) # :nodoc:
config = URI.parse url
adapter = config.scheme
@@ -89,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 self.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_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 0818760b11..b39eb058ae 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -263,7 +263,7 @@ module ActiveRecord
attr_accessor :array
end
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+ module ColumnMethods
def xml(*args)
options = args.extract_options!
column(args[0], 'xml', options)
@@ -325,6 +325,10 @@ module ActiveRecord
def json(name, options = {})
column(name, 'json', options)
end
+ end
+
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+ include ColumnMethods
def column(name, type = nil, options = {})
super
@@ -344,6 +348,10 @@ module ActiveRecord
end
end
+ class Table < ActiveRecord::ConnectionAdapters::Table
+ include ColumnMethods
+ end
+
ADAPTER_NAME = 'PostgreSQL'
NATIVE_DATABASE_TYPES = {
@@ -478,7 +486,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 +500,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 +508,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.
@@ -884,9 +892,13 @@ module ActiveRecord
$1.strip if $1
end
- def table_definition
+ def create_table_definition
TableDefinition.new(self)
end
+
+ def update_table_definition(table_name, base)
+ Table.new(table_name, base)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 6ede5f3390..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
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 6510433056..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
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/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 066f93635a..347f023793 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -367,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
@@ -420,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 a979be6999..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
@@ -146,13 +157,6 @@ module ActiveRecord
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/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb
index 7695eacbff..af4840476c 100644
--- a/activerecord/lib/active_record/railties/controller_runtime.rb
+++ b/activerecord/lib/active_record/railties/controller_runtime.rb
@@ -21,9 +21,10 @@ module ActiveRecord
def cleanup_view_runtime
if ActiveRecord::Base.connected?
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
+ self.db_runtime = (db_runtime || 0) + db_rt_before_render
runtime = super
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
- self.db_runtime = db_rt_before_render + db_rt_after_render
+ self.db_runtime += db_rt_after_render
runtime - db_rt_after_render
else
super
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/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 5cd015eba7..bd783a94cf 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -98,11 +98,6 @@ 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 20e23cb003..4b8c40592e 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -112,7 +112,7 @@ module ActiveRecord
#
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
def includes(*args)
- has_arguments?("includes", args)
+ check_if_method_has_arguments!("includes", args)
spawn.includes!(*args)
end
@@ -130,7 +130,7 @@ module ActiveRecord
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
# "users"."id"
def eager_load(*args)
- has_arguments?("eager_load", args)
+ check_if_method_has_arguments!("eager_load", args)
spawn.eager_load!(*args)
end
@@ -144,7 +144,7 @@ module ActiveRecord
# User.preload(:posts)
# => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
def preload(*args)
- has_arguments?("preload", args)
+ check_if_method_has_arguments!("preload", args)
spawn.preload!(*args)
end
@@ -162,7 +162,7 @@ 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)
- has_arguments?("references", args)
+ check_if_method_has_arguments!("references", args)
spawn.references!(*args)
end
@@ -242,7 +242,7 @@ module ActiveRecord
# 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)
- has_arguments?("group", args)
+ check_if_method_has_arguments!("group", args)
spawn.group!(*args)
end
@@ -273,7 +273,7 @@ module ActiveRecord
# User.order(:name, email: :desc)
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
def order(*args)
- has_arguments?("order", args)
+ check_if_method_has_arguments!("order", args)
spawn.order!(*args)
end
@@ -285,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
@@ -299,7 +304,7 @@ module ActiveRecord
#
# generates a query with 'ORDER BY name ASC, id ASC'.
def reorder(*args)
- has_arguments?("reorder", args)
+ check_if_method_has_arguments!("reorder", args)
spawn.reorder!(*args)
end
@@ -322,7 +327,7 @@ module ActiveRecord
# 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)
- has_arguments?("joins", args)
+ check_if_method_has_arguments!("joins", args)
spawn.joins!(*args.compact.flatten)
end
@@ -935,10 +940,10 @@ module ActiveRecord
# passed into that method as an input. For example:
#
# def references(*args)
- # has_arguments?("references", args)
+ # check_if_method_has_arguments!("references", args)
# ...
# end
- def has_arguments?(method_name, args)
+ def check_if_method_has_arguments!(method_name, args)
if args.blank?
raise ArgumentError, "The method .#{method_name}() must contain arguments."
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 4b7a388dc7..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
@@ -378,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/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 6640f9b497..ad98d7c8ce 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -65,6 +65,21 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
assert_equal :hstore, @column.type
end
+ def test_change_table_supports_hstore
+ @connection.transaction do
+ @connection.change_table('hstores') do |t|
+ t.hstore 'users', default: ''
+ end
+ Hstore.reset_column_information
+ column = Hstore.columns.find { |c| c.name == 'users' }
+ assert_equal :hstore, column.type
+
+ raise ActiveRecord::Rollback # reset the schema change
+ end
+ ensure
+ Hstore.reset_column_information
+ end
+
def test_type_cast_hstore
assert @column
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index d64037eec0..6fc08ae4f0 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -31,6 +31,21 @@ class PostgresqlJSONTest < ActiveRecord::TestCase
assert_equal :json, @column.type
end
+ def test_change_table_supports_json
+ @connection.transaction do
+ @connection.change_table('json_data_type') do |t|
+ t.json 'users', default: '{}'
+ end
+ JsonDataType.reset_column_information
+ column = JsonDataType.columns.find { |c| c.name == 'users' }
+ assert_equal :json, column.type
+
+ raise ActiveRecord::Rollback # reset the schema change
+ end
+ ensure
+ JsonDataType.reset_column_information
+ end
+
def test_type_cast_json
assert @column
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 3bf9125013..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
@@ -1168,4 +1173,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
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 fbc66540d6..af1845c937 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1447,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 0a04117795..c8dfc3244b 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -43,27 +43,6 @@ module ActiveRecord
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)
- end
-
def test_encoded_password
password = 'am@z1ng_p@ssw0rd#!'
encoded_password = URI.encode_www_form_component(password)
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 88bea2211d..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
@@ -96,10 +96,18 @@ module ActiveRecord
add_index "test_models", ["hat_style", "hat_size"], unique: true
rename_column "test_models", "hat_size", 'size'
- assert_equal ['index_test_models_on_hat_style_and_size'], connection.indexes('test_models').map(&:name)
+ 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'
- assert_equal ['index_test_models_on_style_and_size'], connection.indexes('test_models').map(&:name)
+ 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
@@ -128,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)
@@ -245,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/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 0ad05223d4..3dd11ae89d 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, FakeColumn.new(:float))
+ assert_equal float.to_s, @quoter.quote(float, Object.new)
end
def test_quote_fixnum
fixnum = 1
assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
- assert_equal fixnum.to_s, @quoter.quote(fixnum, FakeColumn.new(:integer))
+ assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new)
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, FakeColumn.new(:integer))
+ assert_equal bignum.to_s, @quoter.quote(bignum, Object.new)
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, FakeColumn.new(:decimal))
+ assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new)
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, FakeColumn.new(:date))
+ assert_equal "'lol'", @quoter.quote(Date.today, Object.new)
assert_equal "'lol'", @quoter.quote(Time.now, nil)
- assert_equal "'lol'", @quoter.quote(Time.now, FakeColumn.new(:time))
+ assert_equal "'lol'", @quoter.quote(Time.now, Object.new)
assert_equal "'lol'", @quoter.quote(DateTime.now, nil)
- assert_equal "'lol'", @quoter.quote(DateTime.now, FakeColumn.new(:datetime))
+ assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new)
end
def test_crazy_object
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 53cdf89b1f..c43c7601a2 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -108,30 +108,5 @@ 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 8e6c38706f..7388324a0d 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 Arel.sql("50000"), wheres[:salary]
+ assert_equal 50000, wheres[:salary]
end
def test_default_scope_with_module_includes
wheres = ModuleIncludedPoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal Arel.sql("50000"), wheres[:salary]
+ assert_equal 50000, wheres[:salary]
end
def test_default_scope_with_multiple_calls
wheres = MultiplePoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal Arel.sql("50000"), wheres[:salary]
+ assert_equal 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/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/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 fd5651b4e0..eb4ffd4498 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -312,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/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 d789b6cb7a..cd9835259a 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -540,8 +540,6 @@ 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 daa283e397..c47cb75274 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,17 +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
+* `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*
@@ -24,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'
@@ -51,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)
@@ -84,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.
@@ -98,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*
@@ -122,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:
@@ -139,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`.
@@ -158,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. *Daniele Sluijters*
+* Handle the possible permission denied errors `atomic.rb` might trigger due to its `chown`
+ and `chmod` calls.
-* Hash#extract! returns only those keys that present in the receiver.
+ *Daniele Sluijters*
+
+* `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:
@@ -187,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
@@ -223,6 +269,7 @@
class User
include ActiveSupport::Configurable
+
config_accessor :hair_colors do
[:brown, :black, :blonde, :red]
end
@@ -232,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*
@@ -242,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'
@@ -276,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
@@ -309,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*
@@ -323,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!`.
@@ -358,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 64b23996e8..a28310032a 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -20,7 +20,7 @@ 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.4')
s.add_dependency 'multi_json', '~> 1.3'
s.add_dependency 'tzinfo', '~> 0.3.33'
s.add_dependency 'minitest', '~> 4.2'
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/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/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/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/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/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 9c157ec0b3..463da488f2 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -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_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_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/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/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/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 18baed55b5..52f5232efc 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -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`.
@@ -108,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'
@@ -155,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/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 1d7513a84f..f3c07470af 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,13 +1,22 @@
## Rails 4.0.0 (unreleased) ##
-* fix rake db:* tasks to work with DATABASE_URL and without config/database.yml
- *Terence Lee*
-* The configuration option `config.assets.precompile` is no longer
- in `config/environments/production.rb` but in
- `config/application.rb`.
+## Rails 4.0.0.beta1 (February 25, 2013) ##
- *Yves Senn*
+* 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.
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/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 46a6485c44..579af8c6a5 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -1,4 +1,5 @@
require 'rails/railtie'
+require 'rails/engine/railties'
require 'active_support/core_ext/module/delegation'
require 'pathname'
require 'rbconfig'
@@ -467,7 +468,7 @@ module Rails
end
def railties
- @railties ||= self.class::Railties.new
+ @railties ||= Railties.new
end
# Returns a module with all the helpers defined for the engine.
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 99d80b3245..4e703151ba 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -19,9 +19,6 @@ module Rails
argument :app_path, type: :string
def self.add_shared_options_for(name)
- class_option :builder, type: :string, aliases: '-b',
- desc: "Path to some #{name} builder (can be a filesystem path or URL)"
-
class_option :template, type: :string, aliases: '-m',
desc: "Path to some #{name} template (can be a filesystem path or URL)"
@@ -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/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 1275bce7e6..daf399a538 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -31,10 +31,6 @@ module <%= app_const_base %>
# Disable the asset pipeline.
config.assets.enabled = false
-<% else %>
- # Precompile additional assets.
- # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
- # config.assets.precompile += %w( search.js )
<% end -%>
end
end
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 1f0111368b..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
@@ -59,6 +59,12 @@
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = "http://assets.example.com"
+ <%- unless options.skip_sprockets? -%>
+ # Precompile additional assets.
+ # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
+ # config.assets.precompile += %w( search.js )
+ <%- end -%>
+
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
@@ -70,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/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index af00748037..5fe01d0961 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -94,6 +94,11 @@ task default: :test
end
end
+ def test_dummy_assets
+ template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true
+ template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
+ end
+
def test_dummy_clean
inside dummy_path do
remove_file ".gitignore"
@@ -112,7 +117,7 @@ task default: :test
def stylesheets
if mountable?
- copy_file "#{app_templates_dir}/app/assets/stylesheets/application.css",
+ copy_file "rails/stylesheets.css",
"app/assets/stylesheets/#{name}/application.css"
elsif full?
empty_directory_with_keep_file "app/assets/stylesheets/#{name}"
@@ -123,8 +128,8 @@ task default: :test
return if options.skip_javascript?
if mountable?
- template "#{app_templates_dir}/app/assets/javascripts/application.js.tt",
- "app/assets/javascripts/#{name}/application.js"
+ template "rails/javascripts.js",
+ "app/assets/javascripts/#{name}/application.js"
elsif full?
empty_directory_with_keep_file "app/assets/javascripts/#{name}"
end
@@ -263,6 +268,7 @@ task default: :test
build(:generate_test_dummy)
store_application_definition!
build(:test_dummy_config)
+ build(:test_dummy_assets)
build(:test_dummy_clean)
# ensure that bin/rails has proper dummy_path
build(:bin, true)
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
index e956d13d8a..f7c12e67dd 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
@@ -19,9 +19,6 @@ Gem::Specification.new do |s|
<% end -%>
<%= '# ' if options.dev? || options.edge? -%>s.add_dependency "rails", "~> <%= Rails::VERSION::STRING %>"
-<% if engine? && !options[:skip_javascript] -%>
- # s.add_dependency "<%= "#{options[:javascript]}-rails" %>"
-<% end -%>
<% unless options[:skip_active_record] -%>
s.add_development_dependency "<%= gem_for_database %>"
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
index a8b5bfaf3f..3f2b78f2fd 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
@@ -2,9 +2,6 @@ source "https://rubygems.org"
<% if options[:skip_gemspec] -%>
<%= '# ' if options.dev? || options.edge? -%>gem "rails", "~> <%= Rails::VERSION::STRING %>"
-<% if engine? && !options[:skip_javascript] -%>
-# gem "<%= "#{options[:javascript]}-rails" %>"
-<% end -%>
<% else -%>
# Declare your gem's dependencies in <%= name %>.gemspec.
# Bundler will treat runtime dependencies like base dependencies, and
@@ -12,11 +9,6 @@ source "https://rubygems.org"
gemspec
<% end -%>
-<% unless options[:javascript] == 'jquery' -%>
-# jquery-rails is used by the dummy application
-gem "jquery-rails"
-
-<% end -%>
<% if options[:skip_gemspec] -%>
group :development do
gem "<%= gem_for_database %>"
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/plugin_new/templates/rails/javascripts.js b/railties/lib/rails/generators/rails/plugin_new/templates/rails/javascripts.js
new file mode 100644
index 0000000000..084d5d1c49
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/rails/javascripts.js
@@ -0,0 +1,13 @@
+// This is a manifest file that'll be compiled into application.js, which will include all the files
+// listed below.
+//
+// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
+// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
+//
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// compiled file.
+//
+// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
+// GO AFTER THE REQUIRES BELOW.
+//
+//= require_tree .
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/stylesheets.css b/railties/lib/rails/generators/rails/plugin_new/templates/rails/stylesheets.css
new file mode 100644
index 0000000000..3192ec897b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/rails/stylesheets.css
@@ -0,0 +1,13 @@
+/*
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
+ * listed below.
+ *
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
+ *
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
+ * compiled file, but it's generally better to create a new file per style scope.
+ *
+ *= require_self
+ *= require_tree .
+ */
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 72281a2fef..73e89086a5 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -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/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/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..34441ef679 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -63,13 +63,24 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_no_directory "test/integration/"
assert_no_file "test"
- assert_no_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile")))
+ assert_file "Rakefile" do |contents|
+ assert_no_match(/APP_RAKEFILE/, contents)
+ end
end
def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files
run_generator [destination_root, "-T", "--mountable", '--dummy-path', 'my_dummy_app']
+ assert_file "Rakefile", /APP_RAKEFILE/
+ end
+
+ def test_generating_adds_dummy_app_without_javascript_and_assets_deps
+ run_generator [destination_root]
+
+ assert_file "test/dummy/app/assets/stylesheets/application.css"
- assert_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile")))
+ assert_file "test/dummy/app/assets/javascripts/application.js" do |contents|
+ assert_no_match(/jquery/, contents)
+ end
end
def test_ensure_that_plugin_options_are_not_passed_to_app_generator
@@ -112,7 +123,9 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
def test_ensure_that_skip_active_record_option_is_passed_to_app_generator
run_generator [destination_root, "--skip_active_record"]
assert_no_file "test/dummy/config/database.yml"
- assert_no_match(/ActiveRecord/, File.read(File.join(destination_root, "test/test_helper.rb")))
+ assert_file "test/test_helper.rb" do |contents|
+ assert_no_match /ActiveRecord/, contents
+ end
end
def test_ensure_that_database_option_is_passed_to_app_generator
@@ -134,8 +147,6 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
def test_skipping_javascripts_without_mountable_option
run_generator
assert_no_file "app/assets/javascripts/bukkits/application.js"
- assert_no_file "vendor/assets/javascripts/jquery.js"
- assert_no_file "vendor/assets/javascripts/jquery_ujs.js"
end
def test_javascripts_generation
@@ -143,33 +154,9 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_file "app/assets/javascripts/bukkits/application.js"
end
- def test_jquery_is_the_default_javascript_library
- run_generator [destination_root, "--mountable"]
- assert_file "app/assets/javascripts/bukkits/application.js" do |contents|
- assert_match %r{^//= require jquery}, contents
- assert_match %r{^//= require jquery_ujs}, contents
- end
- assert_file 'bukkits.gemspec' do |contents|
- assert_match(/jquery-rails/, contents)
- end
- end
-
- def test_other_javascript_libraries
- run_generator [destination_root, "--mountable", '-j', 'prototype']
- assert_file "app/assets/javascripts/bukkits/application.js" do |contents|
- assert_match %r{^//= require prototype}, contents
- assert_match %r{^//= require prototype_ujs}, contents
- end
- assert_file 'bukkits.gemspec' do |contents|
- assert_match(/prototype-rails/, contents)
- end
- end
-
def test_skip_javascripts
run_generator [destination_root, "--skip-javascript", "--mountable"]
assert_no_file "app/assets/javascripts/bukkits/application.js"
- assert_no_file "vendor/assets/javascripts/jquery.js"
- assert_no_file "vendor/assets/javascripts/jquery_ujs.js"
end
def test_template_from_dir_pwd
@@ -318,16 +305,6 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_no_match('gemspec', contents)
assert_match(/gem "rails", "~> #{Rails::VERSION::STRING}"/, contents)
assert_match(/group :development do\n gem "sqlite3"\nend/, contents)
- assert_match(/# gem "jquery-rails"/, contents)
- assert_no_match(/# jquery-rails is used by the dummy application\ngem "jquery-rails"/, contents)
- end
- end
-
- def test_skipping_gemspec_in_full_mode_with_javascript_option
- run_generator [destination_root, "--skip-gemspec", "--full", "--javascript=prototype"]
- assert_file "Gemfile" do |contents|
- assert_match(/# gem "prototype-rails"/, contents)
- assert_match(/# jquery-rails is used by the dummy application\ngem "jquery-rails"/, contents)
end
end
@@ -371,38 +348,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/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 2081f4aba8..2724882a23 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -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 = "https://gist.github.com/josevalim/103208/raw/"
- 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/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