diff --git a/.travis.yml b/.travis.yml index 4233b136a839e..9e7a449010a4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: rvm: - 1.9.3 - 2.0.0 - - 2.1.0 + - 2.1.1 - rbx-2 - jruby env: @@ -35,4 +35,3 @@ notifications: bundler_args: --path vendor/bundle --without test services: - memcached - diff --git a/Gemfile b/Gemfile index f327ab7e775cc..f2076f29b1f31 100644 --- a/Gemfile +++ b/Gemfile @@ -8,12 +8,16 @@ gemspec gem 'mocha', '~> 0.14', require: false gem 'rack-cache', '~> 1.2' -gem 'bcrypt-ruby', '~> 3.1.2' gem 'jquery-rails', '~> 3.1.0' gem 'turbolinks' gem 'coffee-rails', '~> 4.0.0' gem 'arel', github: 'rails/arel' +# require: false so bcrypt is loaded only when has_secure_password is used. +# This is to avoid ActiveModel (and by extension the entire framework) +# being dependent on a binary library. +gem 'bcrypt', '~> 3.1.7', require: false + # This needs to be with require false to avoid # it being automatically loaded by sprockets gem 'uglifier', '>= 1.3.0', require: false diff --git a/RAILS_VERSION b/RAILS_VERSION index ee00187eb3410..59e61f95df967 100644 --- a/RAILS_VERSION +++ b/RAILS_VERSION @@ -1 +1 @@ -4.1.0.beta2 +4.2.0.alpha diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc index 664505f60d7b2..9b06d0d162477 100644 --- a/RELEASING_RAILS.rdoc +++ b/RELEASING_RAILS.rdoc @@ -203,34 +203,3 @@ There are two simple steps for fixing the CI: 2. Fix it Repeat these steps until the CI is green. - -=== Manually trigger docs generation - -We have a post-receive hook in GitHub that calls the docs server on pushes. -It triggers generation and publication of edge docs, updates the contrib app, -and generates and publishes stable docs if a new stable tag is detected. - -The hook unfortunately is not invoked by tag pushing, so once the new stable -tag has been pushed to origin, please run - - rake publish_docs - -You should see something like this: - - Rails master hook tasks scheduled: - - * updates the local checkout - * updates Rails Contributors - * generates and publishes edge docs - - If a new stable tag is detected it also - - * generates and publishes stable docs - - This needs typically a few minutes. - -Note you do not need to specify the tag, the docs server figures it out. - -Also, don't worry if you call that multiple times or the hook is triggered -again by some immediate regular push, if the scripts are running new calls -are just queued (in a queue of size 1). diff --git a/Rakefile b/Rakefile index 07d44fc94b4d3..fadebc241f7d2 100644 --- a/Rakefile +++ b/Rakefile @@ -74,7 +74,6 @@ task :update_versions do end end -# # We have a webhook configured in GitHub that gets invoked after pushes. # This hook triggers the following tasks: # @@ -84,11 +83,6 @@ end # * if there's a new stable tag, generates and publishes stable docs # # Everything is automated and you do NOT need to run this task normally. -# -# We publish a new version by tagging, and pushing a tag does not trigger -# that webhook. Stable docs would be updated by any subsequent regular -# push, but if you want that to happen right away just run this. -# desc 'Publishes docs, run this AFTER a new stable tag has been pushed' task :publish_docs do Net::HTTP.new('api.rubyonrails.org', 8080).start do |http| diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 5a61746700371..620369940597b 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,61 +1 @@ -* Support the use of underscored symbols when registering interceptors and - observers like we do elsewhere within Rails. - - *Andrew White* - -* Add the ability to intercept emails before previewing in a similar fashion - to how emails can be intercepted before delivery. - - Fixes #13622. - - Example: - - class CSSInlineStyler - def self.previewing_email(message) - # inline CSS styles - end - end - - ActionMailer::Base.register_preview_interceptor CSSInlineStyler - - *Andrew White* - -* Add mailer previews feature based on 37 Signals mail_view gem. - - *Andrew White* - -* Calling `mail()` without arguments serves as getter for the current mail - message and keeps previously set headers. - - Fixes #13090. - - Example: - - class MailerWithCallback < ActionMailer::Base - after_action :a_callback - - def welcome - mail subject: "subject", to: ["joe@example.com"] - end - - def a_callback - mail # => returns the current mail message - end - end - - *Yves Senn* - -* Instrument the generation of Action Mailer messages. The time it takes to - generate a message is written to the log. - - *Daniel Schierbeck* - -* Invoke mailer defaults as procs only if they are procs, do not convert with - `to_proc`. That an object is convertible to a proc does not mean it's meant - to be always used as a proc. - - Fixes #11533. - - *Alex Tsukernik* - -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/actionmailer/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/actionmailer/CHANGELOG.md) for previous changes. diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index eb8cca9ee4921..18a41ba7a4d92 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -724,11 +724,11 @@ def attachments # format.html # end # - # You can even render text directly without using a template: + # You can even render plain text directly without using a template: # # mail(to: 'mikel@test.lindsaar.net') do |format| - # format.text { render text: "Hello Mikel!" } - # format.html { render text: "

Hello Mikel!

" } + # format.text { render plain: "Hello Mikel!" } + # format.html { render html: "

Hello Mikel!

".html_safe } # end # # Which will render a +multipart/alternative+ email with +text/plain+ and diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 60732c593b155..7b2cf305c2f7d 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -1,7 +1,7 @@ module ActionMailer # Returns the version of the currently loaded ActionMailer as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta2" + Gem::Version.new "4.2.0.alpha" end module VERSION #:nodoc: diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index b05aa21f950b2..68b5213bfc456 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,551 +1 @@ -* Introduce `render :html` as an option to render HTML content with a content - type of `text/html`. This rendering option calls `ERB::Util.html_escape` - internally to escape unsafe HTML string, so you will have to mark your - string as html safe if you have any HTML tag in it. - - Please see #12374 for more detail. - - *Prem Sichanugrist* - -* Introduce `render :plain` as an option to render content with a content type - of `text/plain`. This is the preferred option if you are planning to render - a plain text content. - - Please see #12374 for more detail. - - *Prem Sichanugrist* - -* Introduce `render :body` as an option for sending a raw content back to - browser. Note that this rendering option will unset the default content type - and does not include "Content-Type" header back in the response. - - You should only use this option if you are expecting the "Content-Type" - header to not be set. More information on "Content-Type" header can be found - on RFC 2616, section 7.2.1. - - Please see #12374 for more detail. - - *Prem Sichanugrist* - -* Set stream status to 500 (or 400 on BadRequest) when an error is thrown - before commiting. - - Fixes #12552. - - *Kevin Casey* - -* Add new config option `config.action_dispatch.cookies_serializer` for - specifying a serializer for the signed and encrypted cookie jars. - - The possible values are: - - * `:json` - serialize cookie values with `JSON` - * `:marshal` - serialize cookie values with `Marshal` - * `:hybrid` - transparently migrate existing `Marshal` cookie values to `JSON` - - For new apps `:json` option is added by default and `:marshal` is used - when no option is specified to maintain backwards compatibility. - - *Łukasz Sarnacki*, *Matt Aimonetti*, *Guillermo Iguaran*, *Godfrey Chan*, *Rafael Mendonça França* - -* `FlashHash` now behaves like a `HashWithIndifferentAccess`. - - *Guillermo Iguaran* - -* Set the `:shallow_path` scope option as each scope is generated rather than - waiting until the `shallow` option is set. Also make the behavior of the - `:shallow` resource option consistent with the behavior of the `shallow` method. - - Fixes #12498. - - *Andrew White*, *Aleksi Aalto* - -* Properly require `action_view` in `AbstractController::Rendering` to prevent - uninitialized constant error for `ENCODING_FLAG`. - - *Philipe Fatio* - -* Do not discard query parameters that form a hash with the same root key as - the `wrapper_key` for a request using `wrap_parameters`. - - *Josh Jordan* - -* Ensure that `request.filtered_parameters` is reset between calls to `process` - in `ActionController::TestCase`. - - Fixes #13803. - - *Andrew White* - -* Fix `rake routes` error when `Rails::Engine` with empty routes is mounted. - - Fixes #13810. - - *Maurizio De Santis* - -* Log which keys were affected by deep munge. - - Deep munge solves CVE-2013-0155 security vulnerability, but its - behaviour is definately confusing, so now at least information - about for which keys values were set to nil is visible in logs. - - *Łukasz Sarnacki* - -* Automatically convert dashes to underscores for shorthand routes, e.g: - - get '/our-work/latest' - - When running `rake routes` you will get the following output: - - Prefix Verb URI Pattern Controller#Action - our_work_latest GET /our-work/latest(.:format) our_work#latest - - *Mikko Johansson* - -* Automatically convert dashes to underscores for url helpers, e.g: - - get '/contact-us' => 'pages#contact' - get '/about-us' => 'pages#about_us' - - When running `rake routes` you will get the following output: - - Prefix Verb URI Pattern Controller#Action - contact_us GET /contact-us(.:format) pages#contact - about_us GET /about-us(.:format) pages#about_us - - *Amr Tamimi* - -* Fix stream closing when sending file with `ActionController::Live` included. - - Fixes #12381 - - *Alessandro Diaferia* - -* Allow an absolute controller path inside a module scope. Fixes #12777. - - Example: - - namespace :foo do - # will route to BarController without the namespace. - get '/special', to: '/bar#index' - end - - -* Unique the segment keys array for non-optimized url helpers - - In Rails 3.2 you only needed pass an argument for dynamic segment once so - unique the segment keys array to match the number of args. Since the number - of args is less than required parts the non-optimized code path is selected. - This means to benefit from optimized url generation the arg needs to be - specified as many times as it appears in the path. - - Fixes #12808. - - *Andrew White* - -* Show full route constraints in error message. - - When an optimized helper fails to generate, show the full route constraints - in the error message. Previously it would only show the contraints that were - required as part of the path. - - Fixes #13592. - - *Andrew White* - -* Use a custom route visitor for optimized url generation. Fixes #13349. - - *Andrew White* - -* Allow engine root relative redirects using an empty string. - - Example: - - # application routes.rb - mount BlogEngine => '/blog' - - # engine routes.rb - get '/welcome' => redirect('') - - This now redirects to the path `/blog`, whereas before it would redirect - to the application root path. In the case of a path redirect or a custom - redirect if the path returned contains a host then the path is treated as - absolute. Similarly for option redirects, if the options hash returned - contains a `:host` or `:domain` key then the path is treated as absolute. - - Fixes #7977. - - *Andrew White* - -* Fix `Encoding::CompatibilityError` when public path is UTF-8 - - In #5337 we forced the path encoding to ASCII-8BIT to prevent static file handling - from blowing up before an application has had chance to deal with possibly invalid - urls. However this has a negative side effect of making it an incompatible encoding - if the application's public path has UTF-8 characters in it. - - To work around the problem we check to see if the path has a valid encoding once - it has been unescaped. If it is not valid then we can return early since it will - not match any file anyway. - - Fixes #13518. - - *Andrew White* - -* `ActionController::Parameters#permit!` permits hashes in array values. - - *Xavier Noria* - -* Converts hashes in arrays of unfiltered params to unpermitted params. - - Fixes #13382. - - *Xavier Noria* - -* New config option to opt out of params "deep munging" that was used to - address security vulnerability CVE-2013-0155. In your app config: - - config.action_dispatch.perform_deep_munge = false - - Take care to understand the security risk involved before disabling this. - [Read more.](https://groups.google.com/forum/#!topic/rubyonrails-security/t1WFuuQyavI) - - *Bernard Potocki* - -* `rake routes` shows routes defined under assets prefix. - - *Ryunosuke SATO* - -* Extend cross-site request forgery (CSRF) protection to GET requests with - JavaScript responses, protecting apps from cross-origin ` - - After: - - javascript_include_tag('templates.jst', extname: false ) - # => - - *Nathan Stitt* - -* Fix `current_page?` when the URL contains escaped characters and the - original URL is using the hexadecimal lowercased. - - *Rafael Mendonça França* - -* Fix `text_area` to behave like `text_field` when `nil` is given as - value. - - Before: - - f.text_field :field, value: nil #=> - f.text_area :field, value: nil #=> - - After: - - f.text_area :field, value: nil #=> - - *Joel Cogen* - -* Element of the `grouped_options_for_select` can - optionally contain html attributes as the last element of the array. - - grouped_options_for_select( - [["North America", [['United States','US'],"Canada"], data: { foo: 'bar' }]] - ) - - *Vasiliy Ermolovich* - -* Fix default rendered format problem when calling `render` without :content_type option. - It should return :html. Fix #11393. - - *Gleb Mazovetskiy*, *Oleg*, *kennyj* - -* Fix `link_to` with block and url hashes. - - Before: - - link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') } - # => "Example site" - - After: - - link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') } - # => "Example site" - - *Murahashi Sanemat Kenichi* - -* Fix "Stack Level Too Deep" error when redering recursive partials. - - Fixes #11340. - - *Rafael Mendonça França* - -* Added an `enforce_utf8` hash option for `form_tag` method. - - Control to output a hidden input tag with name `utf8` without monkey - patching. - - Before: - - form_tag - # => '
....
' - - After: - - form_tag - # => '
....
' - - form_tag({}, { :enforce_utf8 => false }) - # => '
....
' - - *ma2gedev* - -* Remove the deprecated `include_seconds` argument from `distance_of_time_in_words`, - pass in an `:include_seconds` hash option to use this feature. - - *Carlos Antonio da Silva* - -* Remove deprecated block passing to `FormBuilder#new`. - - *Vipul A M* - -* Pick `DateField` `DateTimeField` and `ColorField` values from stringified options allowing use of symbol keys with helpers. - - *Jon Rowe* - -* Remove the deprecated `prompt` argument from `grouped_options_for_select`, - pass in a `:prompt` hash option to use this feature. - - *kennyj* - -* Always escape the result of `link_to_unless` method. - - Before: - - link_to_unless(true, 'Showing', 'github.com') - # => "Showing" - - After: - - link_to_unless(true, 'Showing', 'github.com') - # => "<b>Showing</b>" - - *dtaniwaki* - -* Use a case insensitive URI Regexp for #asset_path. - - This fix a problem where the same asset path using different case are generating - different URIs. - - Before: - - image_tag("HTTP://google.com") - # => "\"Google\"" - image_tag("http://google.com") - # => "\"Google\"" - - After: - - image_tag("HTTP://google.com") - # => "\"Google\"" - image_tag("http://google.com") - # => "\"Google\"" - - *David Celis* - -* Element of the `collection_check_boxes` and `collection_radio_buttons` can - optionally contain html attributes as the last element of the array. - - *Vasiliy Ermolovich* - -* Update the HTML `BOOLEAN_ATTRIBUTES` in `ActionView::Helpers::TagHelper` - to conform to the latest HTML 5.1 spec. Add attributes `allowfullscreen`, - `default`, `inert`, `sortable`, `truespeed`, `typemustmatch`. Fix attribute - `seamless` (previously misspelled `seemless`). - - *Alex Peattie* - -* Fix an issue where partials with a number in the filename weren't being digested for cache dependencies. - - *Bryan Ricker* - -* First release, ActionView extracted from ActionPack - - *Piotr Sarnacki*, *Łukasz Strzałkowski* - -Please check [4-0-stable (ActionPack's CHANGELOG)](https://github.com/rails/rails/blob/4-0-stable/actionpack/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/actionview/CHANGELOG.md) for previous changes. diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb index 35d3ba84340fc..6335e3dd4da38 100644 --- a/actionview/lib/action_view/helpers/tags/label.rb +++ b/actionview/lib/action_view/helpers/tags/label.rb @@ -36,7 +36,7 @@ def render(&block) content = @template_object.capture(&block) else content = if @content.blank? - @object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1') + @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1') method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name if object.respond_to?(:to_model) diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 3ccace1274b86..51379d433f624 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -82,7 +82,7 @@ def _back_url # :nodoc: # to using GET. If href: '#' is used and the user has JavaScript # disabled clicking the link will have no effect. If you are relying on the # POST behavior, you should check for it in your controller's action by using - # the request object's methods for post?, delete?, :patch, or put?. + # the request object's methods for post?, delete?, patch?, or put?. # * remote: true - This will allow the unobtrusive JavaScript # driver to make an Ajax request to the URL in question instead of following # the link. The drivers each provide mechanisms for listening for the diff --git a/actionview/lib/action_view/version.rb b/actionview/lib/action_view/version.rb index 3d5d6c9be1e51..b3ff4157752f3 100644 --- a/actionview/lib/action_view/version.rb +++ b/actionview/lib/action_view/version.rb @@ -1,7 +1,7 @@ module ActionView # Returns the version of the currently loaded ActionView as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta2" + Gem::Version.new "4.2.0.alpha" end module VERSION #:nodoc: diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index fe82349265924..b5e9801776f2c 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -143,75 +143,68 @@ def test_label_with_symbols end def test_label_with_locales_strings - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('', label("post", "body")) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('', label("post", "body")) + end end def test_label_with_human_attribute_name - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('', label(:post, :cost)) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('', label(:post, :cost)) + end end def test_label_with_locales_symbols - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('', label(:post, :body)) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('', label(:post, :body)) + end end def test_label_with_locales_and_options - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal( - '', - label(:post, :body, class: "post_body") - ) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal( + '', + label(:post, :body, class: "post_body") + ) + end end def test_label_with_locales_and_value - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('', label(:post, :color, value: "red")) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('', label(:post, :color, value: "red")) + end end def test_label_with_locales_and_nested_attributes - old_locale, I18n.locale = I18n.locale, :label - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:comments) do |cf| - concat cf.label(:body) + with_locale :label do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:comments) do |cf| + concat cf.label(:body) + end end - end - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - '' - end + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '' + end - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale + assert_dom_equal expected, output_buffer + end end def test_label_with_locales_fallback_and_nested_attributes - old_locale, I18n.locale = I18n.locale, :label - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:tags) do |cf| - concat cf.label(:value) + with_locale :label do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:tags) do |cf| + concat cf.label(:value) + end end - end - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - '' - end + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '' + end - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale + assert_dom_equal expected, output_buffer + end end def test_label_with_for_attribute_as_symbol @@ -1811,69 +1804,61 @@ def test_fields_for_with_namespace end def test_submit_with_object_as_new_record_and_locale_strings - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + @post.persisted = false + @post.stubs(:to_key).returns(nil) + form_for(@post) do |f| + concat f.submit + end - @post.persisted = false - @post.stubs(:to_key).returns(nil) - form_for(@post) do |f| - concat f.submit - end + expected = whole_form('/posts', 'new_post', 'new_post') do + "" + end - expected = whole_form('/posts', 'new_post', 'new_post') do - "" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_submit_with_object_as_existing_record_and_locale_strings - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + form_for(@post) do |f| + concat f.submit + end - form_for(@post) do |f| - concat f.submit - end + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do + "" + end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do - "" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_submit_without_object_and_locale_strings - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + form_for(:post) do |f| + concat f.submit class: "extra" + end - form_for(:post) do |f| - concat f.submit class: "extra" - end + expected = whole_form do + "" + end - expected = whole_form do - "" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_submit_with_object_and_nested_lookup - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + form_for(@post, as: :another_post) do |f| + concat f.submit + end - form_for(@post, as: :another_post) do |f| - concat f.submit - end + expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do + "" + end - expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do - "" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_nested_fields_for @@ -2405,6 +2390,18 @@ def test_nested_fields_for_arel_like assert_dom_equal expected, output_buffer end + def test_nested_fields_label_translation_with_more_than_10_records + @post.comments = Array.new(11) { |id| Comment.new(id + 1) } + + I18n.expects(:t).with('post.comments.body', default: [:"comment.body", ''], scope: "helpers.label").times(11).returns "Write body here" + + form_for(@post) do |f| + f.fields_for(:comments) do |cf| + concat cf.label(:body) + end + end + end + def test_nested_fields_for_with_existing_records_on_a_supplied_nested_attributes_collection_different_from_record_one comments = Array.new(2) { |id| Comment.new(id + 1) } @post.comments = [] @@ -3052,4 +3049,11 @@ def whole_form(action = "/", id = nil, html_class = nil, options = {}) def protect_against_forgery? false end + + def with_locale(testing_locale = :label) + old_locale, I18n.locale = I18n.locale, testing_locale + yield + ensure + I18n.locale = old_locale + end end diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 500d8dc42fcb6..f9246e6c8167c 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,72 +1 @@ -* `#to_param` returns `nil` if `#to_key` returns `nil`. Fixes #11399. - - *Yves Senn* - -* Ability to specify multiple contexts when defining a validation. - - Example: - - class Person - include ActiveModel::Validations - - attr_reader :name - validates_presence_of :name, on: [:verify, :approve] - end - - person = Person.new - person.valid? # => true - person.valid?(:verify) # => false - person.errors.full_messages_for(:name) # => ["Name can't be blank"] - person.valid?(:approve) # => false - person.errors.full_messages_for(:name) # => ["Name can't be blank"] - - *Vince Puzzella* - -* `attribute_changed?` now accepts a hash to check if the attribute was - changed `:from` and/or `:to` a given value. - - Example: - - model.name_changed?(from: "Pete", to: "Ringo") - - *Tejas Dinkar* - -* Fix `has_secure_password` to honor bcrypt-ruby's cost attribute. - - *T.J. Schuck* - -* Updated the `ActiveModel::Dirty#changed_attributes` method to be indifferent between using - symbols and strings as keys. - - *William Myers* - -* Added new API methods `reset_changes` and `changes_applied` to `ActiveModel::Dirty` - that control changes state. Previsously you needed to update internal - instance variables, but now API methods are available. - - *Bogdan Gusiev* - -* Fix `has_secure_password` not to trigger `password_confirmation` validations - if no `password_confirmation` is set. - - *Vladimir Kiselev* - -* `inclusion` / `exclusion` validations with ranges will only use the faster - `Range#cover` for numerical ranges, and the more accurate `Range#include?` - for non-numerical ones. - - Fixes range validations like `:a..:f` that used to pass with values like `:be`. - Fixes #10593. - - *Charles Bergeron* - -* Fix regression in `has_secure_password`. When a password is set, but a - confirmation is an empty string, it would incorrectly save. - - *Steve Klabnik* and *Phillip Calvin* - -* Deprecate `Validator#setup`. This should be done manually now in the validator's constructor. - - *Nick Sutterer* - -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activemodel/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activemodel/CHANGELOG.md) for previous changes. diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 01739d8ae4ad8..826e89bf9de03 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -20,9 +20,9 @@ module ClassMethods # value to the password_confirmation attribute and the validation # will not be triggered. # - # You need to add bcrypt-ruby (~> 3.1.2) to Gemfile to use #has_secure_password: + # You need to add bcrypt (~> 3.1.7) to Gemfile to use #has_secure_password: # - # gem 'bcrypt-ruby', '~> 3.1.2' + # gem 'bcrypt', '~> 3.1.7' # # Example using Active Record (which automatically includes ActiveModel::SecurePassword): # @@ -42,13 +42,13 @@ module ClassMethods # User.find_by(name: 'david').try(:authenticate, 'notright') # => false # User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user def has_secure_password(options = {}) - # Load bcrypt-ruby only when has_secure_password is used. + # Load bcrypt gem only when has_secure_password is used. # This is to avoid ActiveModel (and by extension the entire framework) # being dependent on a binary library. begin require 'bcrypt' rescue LoadError - $stderr.puts "You don't have bcrypt-ruby installed in your application. Please add it to your Gemfile and run bundle install" + $stderr.puts "You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install" raise end diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb index f7c9534ffbce3..5c9d2bc6bce36 100644 --- a/activemodel/lib/active_model/version.rb +++ b/activemodel/lib/active_model/version.rb @@ -1,7 +1,7 @@ module ActiveModel # Returns the version of the currently loaded ActiveModel as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta2" + Gem::Version.new "4.2.0.alpha" end module VERSION #:nodoc: diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7efd75a239c73..58f680a03f990 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,1858 +1,42 @@ -* Default scopes are no longer overriden by chained conditions. +* `before_add` callbacks are fired before the record is saved on + `has_and_belongs_to_many` assocations *and* on `has_many :through` + associations. Before this change, `before_add` callbacks would be fired + before the record was saved on `has_and_belongs_to_many` associations, but + *not* on `has_many :through` associations. - Before this change when you defined a `default_scope` in a model - it was overriden by chained conditions in the same field. Now it - is merged like any other scope. + Fixes #14144 - Before: +* Fixed STI classes not defining an attribute method if there is a + conflicting private method defined on its ancestors. - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { where state: 'active' } - scope :inactive, -> { where state: 'inactive' } - end + Fixes #11569. - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'active' - - User.where(state: 'inactive') - # SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' - - After: - - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { where state: 'active' } - scope :inactive, -> { where state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active' - - User.where(state: 'inactive') - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive' - - To get the previous behavior it is needed to explicitly remove the - `default_scope` condition using `unscoped`, `unscope`, `rewhere` or - `except`. - - Example: - - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { unscope(where: :state).where(state: 'active') } - scope :inactive, -> { rewhere state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'active' - - User.inactive - # SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' - -* Perform necessary deeper encoding when hstore is inside an array. - - Fixes #11135. - - *Josh Goodall*, *Genadi Samokovarov* - -* Properly detect if a connection is still active before using it - in multi-threaded environments. - - Fixes #12867. - - *Kevin Casey*, *Matthew Draper*, *William (B.J.) Snow Orvis* - -* When inverting add_index use the index name if present instead of - the columns. - - If there are two indices with matching columns and one of them is - explicitly named then reverting the migration adding the named one - would instead drop the unnamed one. - - The inversion of add_index will now drop the index by its name if - it is present. - - *Hubert Dąbrowski* - -* Add flag to disable schema dump after migration. - - Add a config parameter on Active Record named `dump_schema_after_migration` - which is true by default. Now schema dump does not happen at the - end of migration rake task if `dump_schema_after_migration` is false. - - *Emil Soman* - -* `find_in_batches`, `find_each`, `Result#each` and `Enumerable#index_by` now - return an `Enumerator` that can calculate its size. - - See also #13938. - - *Marc-André Lafortune* - -* Make sure transaction state gets reset after a commit operation on the record. - - If a new transaction was open inside a callback, the record was loosing track - of the transaction level state, and it was leaking that state. - - Fixes #12566. - - *arthurnn* - -* Pass `has_and_belongs_to_many` `:autosave` option to - the underlying `has_many :through` association. - - Fixes #13923. - - *Yves Senn* - -* PostgreSQL implementation of `SchemaStatements#index_name_exists?`. - - The database agnostic implementation does not detect with indexes that are - not supported by the ActiveRecord schema dumper. For example, expressions - indexes would not be detected. - - Fixes #11018. - - *Jonathan Baudanza* - -* Parsing PostgreSQL arrays with empty strings now works correctly. - - Previously, if you tried to parse `{"1","","2","","3"}` the result - would be `["1","2","3"]`, removing the empty strings from the array, - which would be incorrect. Now it will correctly produce `["1","","2","","3"]` - as the result of parsing the above PostgreSQL array. - - Fixes #13907. - - *Maurício Linhares* - -* Associations now raise `ArgumentError` on name conflicts. - - Dangerous association names conflicts include instance or class methods already - defined by `ActiveRecord::Base`. - - Example: - - class Car < ActiveRecord::Base - has_many :errors - end - # Will raise ArgumentError. - - Fixes #13217. - - *Lauro Caetano* - -* Fix regressions on `select_*` methods. - When `select_*` methods receive a `Relation` object, they should be able to - get the arel/binds from it. - Also fix regressions on `select_rows` that was ignoring the binds. - - Fixes #7538, #12017, #13731, #12056. - - *arthurnn* - -* Active Record objects can now be correctly dumped, loaded and dumped again - without issues. - - Previously, if you did `YAML.dump`, `YAML.load` and then `YAML.dump` again - in an Active Record model that used serialization it would fail at the last - dump due to the fields not being correctly serialized before being dumped - to YAML. Now it is possible to dump and load the same object as many times - as needed without any issues. - - Fixes #13861. - - *Maurício Linhares* - -* `find_in_batches` now returns an `Enumerator` when called without a block, so that it - can be chained with other `Enumerable` methods. - - *Marc-André Lafortune* - -* `enum` now raises on "dangerous" name conflicts. - - Dangerous name conflicts includes instance or class method conflicts - with methods defined within `ActiveRecord::Base` but not its ancestors, - as well as conflicts with methods generated by other enums on the same - class. - - Fixes #13389. - - *Godfrey Chan* - -* `scope` now raises on "dangerous" name conflicts. - - Similar to dangerous attribute methods, a scope name conflict is - dangerous if it conflicts with an existing class method defined within - `ActiveRecord::Base` but not its ancestors. - - See also #13389. - - *Godfrey Chan*, *Philippe Creux* - -* Correctly send an user provided statement to a `lock!()` call. - - person.lock! 'FOR SHARE NOWAIT' - # Before: SELECT * ... LIMIT 1 FOR UPDATE - # After: SELECT * ... LIMIT 1 FOR SHARE NOWAIT - - Fixes #13788. - - *Maurício Linhares* - -* Handle aliased attributes `select()`, `order()` and `reorder()`. - - *Tsutomu Kuroda* - -* Reset the collection association when calling `reset` on it. - - Before: - - post.comments.loaded? # => true - post.comments.reset - post.comments.loaded? # => true - - After: - - post.comments.loaded? # => true - post.comments.reset - post.comments.loaded? # => false - - Fixes #13777. - - *Kelsey Schlarman* - -* Make enum fields work as expected with the `ActiveModel::Dirty` API. - - Before this change, using the dirty API would have surprising results: - - conversation = Conversation.new - conversation.status = :active - conversation.status = :archived - conversation.status_was # => 0 - - After this change, the same code would result in: - - conversation = Conversation.new - conversation.status = :active - conversation.status = :archived - conversation.status_was # => "active" - - *Rafael Mendonça França* - -* `has_one` and `belongs_to` accessors don't add ORDER BY to the queries - anymore. - - Since Rails 4.0, we add an ORDER BY in the `first` method to ensure - consistent results among different database engines. But for singular - associations this behavior is not needed since we will have one record to - return. As this ORDER BY option can lead some performance issues we are - removing it for singular associations accessors. - - Fixes #12623. - - *Rafael Mendonça França* - -* Prepend table name for column names passed to `Relation#select`. - - Example: - - Post.select(:id) - # Before: => SELECT id FROM "posts" - # After: => SELECT "posts"."id" FROM "posts" - - *Yves Senn* - -* Fail early with "Primary key not included in the custom select clause" - in `find_in_batches`. - - Before this patch, the exception was raised after the first batch was - yielded to the block. This means that you only get it, when you hit the - `batch_size` treshold. This could shadow the issue in development. - - *Alexander Balashov* - -* Ensure `second` through `fifth` methods act like the `first` finder. - - The famous ordinal Array instance methods defined in ActiveSupport - (`first`, `second`, `third`, `fourth`, and `fifth`) are now available as - full-fledged finders in ActiveRecord. The biggest benefit of this is ordering - of the records returned now defaults to the table's primary key in ascending order. - - Fixes #13743. - - Example: - - User.all.second - - # Before - # => 'SELECT "users".* FROM "users"' - - # After - # => SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1' - - User.offset(3).second - - # Before - # => 'SELECT "users".* FROM "users" LIMIT -1 OFFSET 3' # sqlite3 gem - # => 'SELECT "users".* FROM "users" OFFSET 3' # pg gem - # => 'SELECT `users`.* FROM `users` LIMIT 18446744073709551615 OFFSET 3' # mysql2 gem - - # After - # => SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 4' - - *Jason Meller* - -* ActiveRecord states are now correctly restored after a rollback for - models that did not define any transactional callbacks (i.e. - `after_commit`, `after_rollback` or `after_create`). - - Fixes #13744. - - *Godfrey Chan* - -* Make `touch` fire the `after_commit` and `after_rollback` callbacks. - - *Harry Brundage* - -* Enable partial indexes for `sqlite >= 3.8.0`. - - See http://www.sqlite.org/partialindex.html - - *Cody Cutrer* - -* Don't try to get the subclass if the inheritance column doesn't exist - - The `subclass_from_attrs` method is called even if the column specified by - the `inheritance_column` setting doesn't exist. This prevents setting associations - via the attributes hash if the association name clashes with the value of the setting, - typically `:type`. This worked previously in Rails 3.2. - - *Ujjwal Thaakar* - -* Enum mappings are now exposed via class methods instead of constants. - - Example: - - class Conversation < ActiveRecord::Base - enum status: [ :active, :archived ] - end - - Before: - - Conversation::STATUS # => { "active" => 0, "archived" => 1 } - - After: - - Conversation.statuses # => { "active" => 0, "archived" => 1 } - - *Godfrey Chan* - -* Set `NameError#name` when STI-class-lookup fails. - - *Chulki Lee* - -* Fix bug in `becomes!` when changing from the base model to a STI sub-class. - - Fixes #13272. - - *the-web-dev*, *Yves Senn* - -* Currently Active Record can be configured via the environment variable - `DATABASE_URL` or by manually injecting a hash of values which is what Rails does, - reading in `database.yml` and setting Active Record appropriately. Active Record - expects to be able to use `DATABASE_URL` without the use of Rails, and we cannot - rip out this functionality without deprecating. This presents a problem though - when both config is set, and a `DATABASE_URL` is present. Currently the - `DATABASE_URL` should "win" and none of the values in `database.yml` are - used. This is somewhat unexpected, if one were to set values such as - `pool` in the `production:` group of `database.yml` they are ignored. - - There are many ways that Active Record initiates a connection today: - - - Stand Alone (without rails) - - `rake db:` - - `ActiveRecord.establish_connection` - - - With Rails - - `rake db:` - - `rails | ` - - `rails dbconsole` - - Now all of these behave exactly the same way. The best way to do - this is to put all of this logic in one place so it is guaranteed to be used. - - Here is the matrix of how this behavior works: - - ``` - No database.yml - No DATABASE_URL - => Error - ``` - - ``` - database.yml present - No DATABASE_URL - => Use database.yml configuration - ``` - - ``` - No database.yml - DATABASE_URL present - => use DATABASE_URL configuration - ``` - - ``` - database.yml present - DATABASE_URL present - => Merged into `url` sub key. If both specify `url` sub key, the `database.yml` `url` - sub key "wins". If other paramaters `adapter` or `database` are specified in YAML, - they are discarded as the `url` sub key "wins". - ``` - - Current implementation uses `ActiveRecord::Base.configurations` to resolve and merge - all connection information before returning. This is achieved through a utility - class: `ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig`. - - To understand the exact behavior of this class, it is best to review the - behavior in `activerecord/test/cases/connection_adapters/connection_handler_test.rb`. - - *Richard Schneeman* - -* Make `change_column_null` revertable. Fixes #13576. - - *Yves Senn*, *Nishant Modak*, *Prathamesh Sonpatki* - -* Don't create/drop the test database if RAILS_ENV is specified explicitly. - - Previously, when the environment was development, we would always - create or drop both the test and development databases. - - Now, if RAILS_ENV is explicitly defined as development, we don't create - the test database. - - *Damien Mathieu* - -* Initialize version on Migration objects so that it can be used in a migration, - and it will be included in the announce message. - - *Dylan Thacker-Smith* - -* `change_table` now uses the current adapter's `update_table_definition` - method to retrieve a specific table definition. - This ensures that `change_table` and `create_table` will use - similar objects. - - Fixes #13577, #13503. - - *Nishant Modak*, *Prathamesh Sonpatki*, *Rafael Mendonça França* - -* Fixed ActiveRecord::Store nil conversion TypeError when using YAML coder. - In case the YAML passed as paramter is nil, uses an empty string. - - Fixes #13570. - - *Thales Oliveira* - -* Deprecate unused `ActiveRecord::Base.symbolized_base_class` - and `ActiveRecord::Base.symbolized_sti_name` without replacement. - - *Yves Senn* - -* Since the `test_help.rb` file in Railties now automatically maintains - your test schema, the `rake db:test:*` tasks are deprecated. This - doesn't stop you manually running other tasks on your test database - if needed: - - rake db:schema:load RAILS_ENV=test - - *Jon Leighton* - -* Fix presence validator for association when the associated record responds to `to_a`. - - *gmarik* - -* Fixed regression on preload/includes with multiple arguments failing in certain conditions, - raising a NoMethodError internally by calling `reflect_on_association` for `NilClass:Class`. - - Fixes #13437. - - *Vipul A M*, *khustochka* - -* Add the ability to nullify the `enum` column. - - Example: - - class Conversation < ActiveRecord::Base - enum gender: [:female, :male] - end - - Conversation::GENDER # => { female: 0, male: 1 } - - # conversation.update! gender: 0 - conversation.female! - conversation.female? # => true - conversation.gender # => "female" - - # conversation.update! gender: nil - conversation.gender = nil - conversation.gender.nil? # => true - conversation.gender # => nil - - *Amr Tamimi* - -* Connection specification now accepts a "url" key. The value of this - key is expected to contain a database URL. The database URL will be - expanded into a hash and merged. - - *Richard Schneeman* - -* An `ArgumentError` is now raised on a call to `Relation#where.not(nil)`. - - Example: - - User.where.not(nil) - - # Before - # => 'SELECT `users`.* FROM `users` WHERE (NOT (NULL))' - - # After - # => ArgumentError, 'Invalid argument for .where.not(), got nil.' - - *Kuldeep Aggarwal* - -* Deprecated use of string argument as a configuration lookup in - `ActiveRecord::Base.establish_connection`. Instead, a symbol must be given. - - *José Valim* - -* Fixed `update_column`, `update_columns`, and `update_all` to correctly serialize - values for `array`, `hstore` and `json` column types in PostgreSQL. - - Fixes #12261. - - *Tadas Tamosauskas*, *Carlos Antonio da Silva* - -* Do not consider PostgreSQL array columns as number or text columns. - - The code uses these checks in several places to know what to do with a - particular column, for instance AR attribute query methods has a branch - like this: - - if column.number? - !value.zero? - end - - This should never be true for array columns, since it would be the same - as running [].zero?, which results in a NoMethodError exception. - - Fixing this by ensuring that array columns in PostgreSQL never return - true for number?/text? checks. - - *Carlos Antonio da Silva* - -* When connecting to a non-existant database, the error: - `ActiveRecord::NoDatabaseError` will now be raised. When being used with Rails - the error message will include information on how to create a database: - `rake db:create`. Supported adapters: postgresql, mysql, mysql2, sqlite3 - - *Richard Schneeman* - -* Do not raise `'cannot touch on a new record object'` exception on destroying - already destroyed `belongs_to` association with `touch: true` option. - - Fixes #13445. - - Example: - - # Given Comment has belongs_to :post, touch: true - comment.post.destroy - comment.destroy # no longer raises an error - - *Paul Nikitochkin* - -* Fix a bug when assigning an array containing string numbers to a - PostgreSQL integer array column. - - Fixes #13444. - - Example: - - # Given Book#ratings is of type :integer, array: true - Book.new(ratings: [1, 2]) # worked before - Book.new(ratings: ['1', '2']) # now works as well - - *Damien Mathieu* - -* Fix `PostgreSQL` insert to properly extract table name from multiline string SQL. - - Previously, executing an insert SQL in `PostgreSQL` with a command like this: - - insert into articles( - number) - values( - 5152 - ) - - would not work because the adapter was unable to extract the correct `articles` - table name. - - *Kuldeep Aggarwal* - -* Correctly escape PostgreSQL arrays. - - Fixes: CVE-2014-0080 - -* `Relation` no longer has mutator methods like `#map!` and `#delete_if`. Convert - to an `Array` by calling `#to_a` before using these methods. - - It intends to prevent odd bugs and confusion in code that call mutator - methods directly on the `Relation`. - - Example: - - # Instead of this - Author.where(name: 'Hank Moody').compact! - - # Now you have to do this - authors = Author.where(name: 'Hank Moody').to_a - authors.compact! - - *Lauro Caetano* - -* Better support for `where()` conditions that use a `belongs_to` - association name. - - Using the name of an association in `where` previously worked only - if the value was a single `ActiveRecord::Base` object. e.g. - - Post.where(author: Author.first) - - Any other values, including `nil`, would cause invalid SQL to be - generated. This change supports arguments in the `where` query - conditions where the key is a `belongs_to` association name and the - value is `nil`, an `Array` of `ActiveRecord::Base` objects, or an - `ActiveRecord::Relation` object. - - class Post < ActiveRecord::Base - belongs_to :author - end - - `nil` value finds records where the association is not set: - - Post.where(author: nil) - # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IS NULL - - `Array` values find records where the association foreign key - matches the ids of the passed ActiveRecord models, resulting - in the same query as `Post.where(author_id: [1,2])`: - - authors_array = [Author.find(1), Author.find(2)] - Post.where(author: authors_array) - # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IN (1, 2) - - `ActiveRecord::Relation` values find records using the same - query as `Post.where(author_id: Author.where(last_name: "Emde"))` - - Post.where(author: Author.where(last_name: "Emde")) - # SELECT "posts".* FROM "posts" - # WHERE "posts"."author_id" IN ( - # SELECT "authors"."id" FROM "authors" - # WHERE "authors"."last_name" = 'Emde') - - Polymorphic `belongs_to` associations will continue to be handled - appropriately, with the polymorphic `association_type` field added - to the query to match the base class of the value. This feature - previously only worked when the value was a single `ActveRecord::Base`. - - class Post < ActiveRecord::Base - belongs_to :author, polymorphic: true - end - - Post.where(author: Author.where(last_name: "Emde")) - # Generates a query similar to: - Post.where(author_id: Author.where(last_name: "Emde"), author_type: "Author") - - *Martin Emde* - -* Respect temporary option when dropping tables with MySQL. - - Normal DROP TABLE also works, but commits the transaction. - - drop_table :temporary_table, temporary: true - - *Cody Cutrer* - -* Add option to create tables from a query. - - create_table(:long_query, temporary: true, - as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id") - - Generates: - - CREATE TEMPORARY TABLE long_query AS - SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id - - *Cody Cutrer* - -* `db:test:clone` and `db:test:prepare` must load Rails environment. - - `db:test:clone` and `db:test:prepare` use `ActiveRecord::Base`. configurations, - so we need to load the Rails environment, otherwise the config wont be in place. - - *arthurnn* - -* Use the right column to type cast grouped calculations with custom expressions. - - Fixes #13230. - - Example: - - # Before - Account.group(:firm_name).sum('0.01 * credit_limit') - # => { '37signals' => '0.5' } - - # After - Account.group(:firm_name).sum('0.01 * credit_limit') - # => { '37signals' => 0.5 } - - *Paul Nikitochkin* - -* Polymorphic `belongs_to` associations with the `touch: true` option set update the timestamps of - the old and new owner correctly when moved between owners of different types. - - Example: - - class Rating < ActiveRecord::Base - belongs_to :rateable, polymorphic: true, touch: true - end - - rating = Rating.create rateable: Song.find(1) - rating.update_attributes rateable: Book.find(2) # => timestamps of Song(1) and Book(2) are updated - - *Severin Schoepke* - -* Improve formatting of migration exception messages: make them easier to read - with line breaks before/after, and improve the error for pending migrations. - - *John Bachir* - -* Fix `last` with `offset` to return the proper record instead of always the last one. - - Example: - - Model.offset(4).last - # => returns the 4th record from the end. - - Fixes #7441. - - *kostya*, *Lauro Caetano* - -* `type_to_sql` returns a `String` for unmapped columns. This fixes an error - when using unmapped PostgreSQL array types. - - Example: - - change_colum :table, :column, :bigint, array: true - - Fixes #13146. - - *Jens Fahnenbruck*, *Yves Senn* - -* Fix `QueryCache` to work with nested blocks, so that it will only clear the existing cache - after leaving the outer block instead of clearing it right after the inner block is finished. - - *Vipul A M* - -* The ERB in fixture files is no longer evaluated in the context of the main - object. Helper methods used by multiple fixtures should be defined on the - class object returned by `ActiveRecord::FixtureSet.context_class`. - - *Victor Costan* - -* Previously, the `has_one` macro incorrectly accepted the `counter_cache` - option, but never actually supported it. Now it will raise an `ArgumentError` - when using `has_one` with `counter_cache`. - - *Godfrey Chan* - -* Implement `rename_index` natively for MySQL >= 5.7. - - *Cody Cutrer* - -* Fix bug when validating the uniqueness of an aliased attribute. - - Fixes #12402. - - *Lauro Caetano* - -* Update counter cache on a `has_many` relationship regardless of default scope. - - Fixes #12952. - - *Uku Taht* - -* `rename_index` adds the new index before removing the old one. This allows to - rename indexes on columns with a foreign key and prevents the following error: - - Cannot drop index 'index_engines_on_car_id': needed in a foreign key constraint - - *Cody Cutrer*, *Yves Senn* - -* Raise `ActiveRecord::RecordNotDestroyed` when a replaced child - marked with `dependent: destroy` fails to be destroyed. - - Fixes #12812. - - *Brian Thomas Storti* - -* Fix validation on uniqueness of empty association. - - *Evgeny Li* - -* Make `ActiveRecord::Relation#unscope` affect relations it is merged in to. - - *Jon Leighton* - -* Use strings to represent non-string `order_values`. - - *Yves Senn* - -* Checks to see if the record contains the foreign key to set the inverse automatically. - - *Edo Balvers* - -* Added `ActiveRecord::Base.to_param` for convenient "pretty" URLs derived from a model's attribute or method. - - Example: - - class User < ActiveRecord::Base - to_param :name - end - - user = User.find_by(name: 'Fancy Pants') - user.id # => 123 - user.to_param # => "123-fancy-pants" - - *Javan Makhmali* - -* Added `ActiveRecord::Base.no_touching`, which allows ignoring touch on models. - - Example: - - Post.no_touching do - Post.first.touch - end - - *Sam Stephenson*, *Damien Mathieu* - -* Prevent the counter cache from being decremented twice when destroying - a record on a `has_many :through` association. - - Fixes #11079. - - *Dmitry Dedov* - -* Unify boolean type casting for `MysqlAdapter` and `Mysql2Adapter`. - `type_cast` will return `1` for `true` and `0` for `false`. - - Fixes #11119. - - *Adam Williams*, *Yves Senn* - -* Fix bug where `has_one` association record update result in crash, when replaced with itself. - - Fixes #12834. - - *Denis Redozubov*, *Sergio Cambra* - -* Log bind variables after they are type casted. This makes it more - transparent what values are actually sent to the database. - - irb(main):002:0> Event.find("im-no-integer") - # Before: ... WHERE "events"."id" = $1 LIMIT 1 [["id", "im-no-integer"]] - # After: ... WHERE "events"."id" = $1 LIMIT 1 [["id", 0]] - - *Yves Senn* - -* Fix uninitialized constant `TransactionState` error when `Marshall.load` is used on an Active Record result. - - Fixes #12790. - - *Jason Ayre* - -* `.unscope` now removes conditions specified in `default_scope`. - - *Jon Leighton* - -* Added `ActiveRecord::QueryMethods#rewhere` which will overwrite an existing, named where condition. - - Examples: - - Post.where(trashed: true).where(trashed: false) #=> WHERE `trashed` = 1 AND `trashed` = 0 - Post.where(trashed: true).rewhere(trashed: false) #=> WHERE `trashed` = 0 - Post.where(active: true).where(trashed: true).rewhere(trashed: false) #=> WHERE `active` = 1 AND `trashed` = 0 - - *DHH* - -* Extend `ActiveRecord::Base#cache_key` to take an optional list of timestamp attributes of which the highest will be used. - - Example: - - # last_reviewed_at will be used, if that's more recent than updated_at, or vice versa - Person.find(5).cache_key(:updated_at, :last_reviewed_at) - - *DHH* - -* Added `ActiveRecord::Base#enum` for declaring enum attributes where the values map to integers in the database, but can be queried by name. - - Example: - - class Conversation < ActiveRecord::Base - enum status: [:active, :archived] - end - - Conversation::STATUS # => { active: 0, archived: 1 } - - # conversation.update! status: 0 - conversation.active! - conversation.active? # => true - conversation.status # => "active" - - # conversation.update! status: 1 - conversation.archived! - conversation.archived? # => true - conversation.status # => "archived" - - # conversation.update! status: 1 - conversation.status = :archived - - *DHH* - -* `ActiveRecord::Base#attribute_for_inspect` now truncates long arrays (more than 10 elements). - - *Jan Bernacki* - -* Allow for the name of the `schema_migrations` table to be configured. - - *Jerad Phelps* - -* Do not add to scope includes values from through associations. - Fixed bug when providing `includes` in through association scope, and fetching targets. - - Example: - - class Vendor < ActiveRecord::Base - has_many :relationships, -> { includes(:user) } - has_many :users, through: :relationships - end - - vendor = Vendor.first - - # Before - - vendor.users.to_a # => Raises exception: not found `:user` for `User` - - # After - - vendor.users.to_a # => No exception is raised - - Fixes #12242, #9517, #10240. - - *Paul Nikitochkin* - -* Type cast json values on write, so that the value is consistent - with reading from the database. - - Example: - - x = JsonDataType.new tags: {"string" => "foo", :symbol => :bar} - - # Before: - x.tags # => {"string" => "foo", :symbol => :bar} - - # After: - x.tags # => {"string" => "foo", "symbol" => "bar"} - - *Severin Schoepke* - -* `ActiveRecord::Store` works together with PostgreSQL `hstore` columns. - - Fixes #12452. - - *Yves Senn* - -* Fix bug where `ActiveRecord::Store` used a global `Hash` to keep track of - all registered `stored_attributes`. Now every subclass of - `ActiveRecord::Base` has it's own `Hash`. - - *Yves Senn* - -* Save `has_one` association when primary key is manually set. - - Fixes #12302. - - *Lauro Caetano* - -* Allow any version of BCrypt when using `has_secure_password`. - - *Mike Perham* - -* Sub-query generated for `Relation` passed as array condition did not take in account - bind values and have invalid syntax. - - Generate sub-query with inline bind values. - - Fixes #12586. - - *Paul Nikitochkin* - -* Fix a bug where rake db:structure:load crashed when the path contained - spaces. - - *Kevin Mook* - -* `ActiveRecord::QueryMethods#unscope` unscopes negative equality - - Allows you to call `#unscope` on a relation with negative equality - operators, i.e. `Arel::Nodes::NotIn` and `Arel::Nodes::NotEqual` that have - been generated through the use of `where.not`. - - *Eric Hankins* - -* Raise an exception when model without primary key calls `.find_with_ids`. - - *Shimpei Makimoto* - -* Make `Relation#empty?` use `exists?` instead of `count`. - - *Szymon Nowak* - -* `rake db:structure:dump` no longer crashes when the port was specified as `Fixnum`. - - *Kenta Okamoto* - -* `NullRelation#pluck` takes a list of columns - - The method signature in `NullRelation` was updated to mimic that in - `Calculations`. - - *Derek Prior* - -* `scope_chain` should not be mutated for other reflections. - - Currently `scope_chain` uses same array for building different - `scope_chain` for different associations. During processing - these arrays are sometimes mutated and because of in-place - mutation the changed `scope_chain` impacts other reflections. - - Fix is to dup the value before adding to the `scope_chain`. - - Fixes #3882. - - *Neeraj Singh* - -* Prevent the inversed association from being reloaded on save. - - Fixes #9499. - - *Dmitry Polushkin* - -* Generate subquery for `Relation` if it passed as array condition for `where` - method. - - Example: - - # Before - Blog.where('id in (?)', Blog.where(id: 1)) - # => SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = 1 - # => SELECT "blogs".* FROM "blogs" WHERE (id IN (1)) - - # After - Blog.where('id in (?)', Blog.where(id: 1).select(:id)) - # => SELECT "blogs".* FROM "blogs" - # WHERE "blogs"."id" IN (SELECT "blogs"."id" FROM "blogs" WHERE "blogs"."id" = 1) - - Fixes #12415. - - *Paul Nikitochkin* - -* For missed association exception message - which is raised in `ActiveRecord::Associations::Preloader` class - added owner record class name in order to simplify to find problem code. - - *Paul Nikitochkin* - -* `has_and_belongs_to_many` is now transparently implemented in terms of - `has_many :through`. Behavior should remain the same, if not, it is a bug. - -* `create_savepoint`, `rollback_to_savepoint` and `release_savepoint` accept - a savepoint name. - - *Yves Senn* - -* Make `next_migration_number` accessible for third party generators. - - *Yves Senn* - -* Objects instantiated using a null relationship will now retain the - attributes of the where clause. - - Fixes #11676, #11675, #11376. - - *Paul Nikitochkin*, *Peter Brown*, *Nthalk* - -* Fixed `ActiveRecord::Associations::CollectionAssociation#find` - when using `has_many` association with `:inverse_of` and finding an array of one element, - it should return an array of one element too. - - *arthurnn* - -* Callbacks on has_many should access the in memory parent if a inverse_of is set. - - *arthurnn* - -* `ActiveRecord::ConnectionAdapters.string_to_time` respects - string with timezone (e.g. Wed, 04 Sep 2013 20:30:00 JST). - - Fixes #12278. - - *kennyj* - -* Calling `update_attributes` will now throw an `ArgumentError` whenever it - gets a `nil` argument. More specifically, it will throw an error if the - argument that it gets passed does not respond to to `stringify_keys`. - - Example: - - @my_comment.update_attributes(nil) # => raises ArgumentError - - *John Wang* - -* Deprecate `quoted_locking_column` method, which isn't used anywhere. - - *kennyj* - -* Migration dump UUID default functions to schema.rb. - - Fixes #10751. - - *kennyj* - -* Fixed a bug in `ActiveRecord::Associations::CollectionAssociation#find_by_scan` - when using `has_many` association with `:inverse_of` option and UUID primary key. - - Fixes #10450. - - *kennyj* - -* Fix: joins association, with defined in the scope block constraints by using several - where constraints and at least of them is not `Arel::Nodes::Equality`, - generates invalid SQL expression. - - Fixes #11963. - - *Paul Nikitochkin* - -* `CollectionAssociation#first`/`#last` (e.g. `has_many`) use a `LIMIT`ed - query to fetch results rather than loading the entire collection. - - *Lann Martin* - -* Make possible to run SQLite rake tasks without the `Rails` constant defined. - - *Damien Mathieu* - -* Allow Relation#from to accept other relations with bind values. - - *Ryan Wallace* - -* Fix inserts with prepared statements disabled. - - Fixes #12023. - - *Rafael Mendonça França* - -* Setting a has_one association on a new record no longer causes an empty - transaction. - - *Dylan Thacker-Smith* - -* Fix `AR::Relation#merge` sometimes failing to preserve `readonly(false)` flag. - - *thedarkone* - -* Re-use `order` argument pre-processing for `reorder`. - - *Paul Nikitochkin* - -* Fix PredicateBuilder so polymorphic association keys in `where` clause can - accept objects other than direct descendants of `ActiveRecord::Base` (decorated - models, for example). - - *Mikhail Dieterle* - -* PostgreSQL adapter recognizes negative money values formatted with - parentheses (eg. `($1.25) # => -1.25`)). - Fixes #11899. - - *Yves Senn* - -* Stop interpreting SQL 'string' columns as :string type because there is no - common STRING datatype in SQL. - - *Ben Woosley* - -* `ActiveRecord::FinderMethods#exists?` returns `true`/`false` in all cases. - - *Xavier Noria* - -* Assign inet/cidr attribute with `nil` value for invalid address. - - Example: - - record = User.new - record.logged_in_from_ip # is type of an inet or a cidr - - # Before: - record.logged_in_from_ip = 'bad ip address' # raise exception - - # After: - record.logged_in_from_ip = 'bad ip address' # do not raise exception - record.logged_in_from_ip # => nil - record.logged_in_from_ip_before_type_cast # => 'bad ip address' - - *Paul Nikitochkin* - -* `add_to_target` now accepts a second optional `skip_callbacks` argument - - If truthy, it will skip the :before_add and :after_add callbacks. - - *Ben Woosley* - -* Fix interactions between `:before_add` callbacks and nested attributes - assignment of `has_many` associations, when the association was not - yet loaded: - - - A `:before_add` callback was being called when a nested attributes - assignment assigned to an existing record. - - - Nested Attributes assignment did not affect the record in the - association target when a `:before_add` callback triggered the - loading of the association - - *Jörg Schray* - -* Allow enable_extension migration method to be revertible. - - *Eric Tipton* - -* Type cast hstore values on write, so that the value is consistent - with reading from the database. - - Example: - - x = Hstore.new tags: {"bool" => true, "number" => 5} - - # Before: - x.tags # => {"bool" => true, "number" => 5} - - # After: - x.tags # => {"bool" => "true", "number" => "5"} - - *Yves Senn* , *Severin Schoepke* - -* Fix multidimensional PostgreSQL arrays containing non-string items. - - *Yves Senn* - -* Fixes bug when using includes combined with select, the select statement was overwritten. - - Fixes #11773. - - *Edo Balvers* - -* Load fixtures from linked folders. - - *Kassio Borges* - -* Create a directory for sqlite3 file if not present on the system. - - *Richard Schneeman* - -* Removed redundant override of `xml` column definition for PostgreSQL, - in order to use `xml` column type instead of `text`. - - *Paul Nikitochkin*, *Michael Nikitochkin* - -* Revert `ActiveRecord::Relation#order` change that make new order - prepend the old one. - - Before: - - User.order("name asc").order("created_at desc") - # SELECT * FROM users ORDER BY created_at desc, name asc - - After: - - User.order("name asc").order("created_at desc") - # SELECT * FROM users ORDER BY name asc, created_at desc - - This also affects order defined in `default_scope` or any kind of associations. - -* Add ability to define how a class is converted to Arel predicates. - For example, adding a very vendor specific regex implementation: - - regex_handler = proc do |column, value| - Arel::Nodes::InfixOperation.new('~', column, value.source) - end - ActiveRecord::PredicateBuilder.register_handler(Regexp, regex_handler) - - *Sean Griffin & @joannecheng* - -* Don't allow `quote_value` to be called without a column. - - Some adapters require column information to do their job properly. - By enforcing the provision of the column for this internal method - we ensure that those using adapters that require column information - will always get the proper behavior. - - *Ben Woosley* - -* When using optimistic locking, `update` was not passing the column to `quote_value` - to allow the connection adapter to properly determine how to quote the value. This was - affecting certain databases that use specific column types. - - Fixes #6763. - - *Alfred Wong* - -* rescue from all exceptions in `ConnectionManagement#call` - - Fixes #11497. - - As `ActiveRecord::ConnectionAdapters::ConnectionManagement` middleware does - not rescue from Exception (but only from StandardError), the Connection - Pool quickly runs out of connections when multiple erroneous Requests come - in right after each other. - - Rescuing from all exceptions and not just StandardError, fixes this - behaviour. - - *Vipul A M* - -* `change_column` for PostgreSQL adapter respects the `:array` option. - - *Yves Senn* - -* Remove deprecation warning from `attribute_missing` for attributes that are columns. - - *Arun Agrawal* - -* Remove extra decrement of transaction deep level. - - Fixes #4566. - - *Paul Nikitochkin* - -* Reset @column_defaults when assigning `locking_column`. - We had a potential problem. For example: - - class Post < ActiveRecord::Base - self.column_defaults # if we call this unintentionally before setting locking_column ... - self.locking_column = 'my_locking_column' - end - - Post.column_defaults["my_locking_column"] - => nil # expected value is 0 ! - - *kennyj* - -* Remove extra select and update queries on save/touch/destroy ActiveRecord model - with belongs to reflection with option `touch: true`. - - Fixes #11288. - - *Paul Nikitochkin* - -* Remove deprecated nil-passing to the following `SchemaCache` methods: - `primary_keys`, `tables`, `columns` and `columns_hash`. - - *Yves Senn* - -* Remove deprecated block filter from `ActiveRecord::Migrator#migrate`. - - *Yves Senn* - -* Remove deprecated String constructor from `ActiveRecord::Migrator`. - - *Yves Senn* - -* Remove deprecated `scope` use without passing a callable object. - - *Arun Agrawal* - -* Remove deprecated `transaction_joinable=` in favor of `begin_transaction` - with `:joinable` option. - - *Arun Agrawal* - -* Remove deprecated `decrement_open_transactions`. - - *Arun Agrawal* - -* Remove deprecated `increment_open_transactions`. - - *Arun Agrawal* - -* Remove deprecated `PostgreSQLAdapter#outside_transaction?` - method. You can use `#transaction_open?` instead. - - *Yves Senn* - -* Remove deprecated `ActiveRecord::Fixtures.find_table_name` in favor of - `ActiveRecord::Fixtures.default_fixture_model_name`. - - *Vipul A M* - -* Removed deprecated `columns_for_remove` from `SchemaStatements`. - - *Neeraj Singh* - -* Remove deprecated `SchemaStatements#distinct`. - - *Francesco Rodriguez* - -* Move deprecated `ActiveRecord::TestCase` into the rails test - suite. The class is no longer public and is only used for internal - Rails tests. - - *Yves Senn* - -* Removed support for deprecated option `:restrict` for `:dependent` - in associations. - - *Neeraj Singh* - -* Removed support for deprecated `delete_sql` in associations. - - *Neeraj Singh* - -* Removed support for deprecated `insert_sql` in associations. - - *Neeraj Singh* - -* Removed support for deprecated `finder_sql` in associations. - - *Neeraj Singh* - -* Support array as root element in JSON fields. - - *Alexey Noskov & Francesco Rodriguez* - -* Removed support for deprecated `counter_sql` in associations. - - *Neeraj Singh* - -* Do not invoke callbacks when `delete_all` is called on collection. - - Method `delete_all` should not be invoking callbacks and this - feature was deprecated in Rails 4.0. This is being removed. - `delete_all` will continue to honor the `:dependent` option. However - if `:dependent` value is `:destroy` then the `:delete_all` deletion - strategy for that collection will be applied. - - User can also force a deletion strategy by passing parameter to - `delete_all`. For example you can do `@post.comments.delete_all(:nullify)`. - - *Neeraj Singh* - -* Calling default_scope without a proc will now raise `ArgumentError`. - - *Neeraj Singh* - -* Removed deprecated method `type_cast_code` from Column. - - *Neeraj Singh* - -* Removed deprecated options `delete_sql` and `insert_sql` from HABTM - association. - - Removed deprecated options `finder_sql` and `counter_sql` from - collection association. - - *Neeraj Singh* - -* Remove deprecated `ActiveRecord::Base#connection` method. - Make sure to access it via the class. - - *Yves Senn* - -* Remove deprecation warning for `auto_explain_threshold_in_seconds`. - - *Yves Senn* - -* Remove deprecated `:distinct` option from `Relation#count`. - - *Yves Senn* - -* Removed deprecated methods `partial_updates`, `partial_updates?` and - `partial_updates=`. - - *Neeraj Singh* - -* Removed deprecated method `scoped`. - - *Neeraj Singh* - -* Removed deprecated method `default_scopes?`. - - *Neeraj Singh* - -* Remove implicit join references that were deprecated in 4.0. - - Example: - - # before with implicit joins - Comment.where('posts.author_id' => 7) - - # after - Comment.references(:posts).where('posts.author_id' => 7) - - *Yves Senn* - -* Apply default scope when joining associations. For example: - - class Post < ActiveRecord::Base - default_scope -> { where published: true } - end - - class Comment - belongs_to :post - end - - When calling `Comment.joins(:post)`, we expect to receive only - comments on published posts, since that is the default scope for - posts. - - Before this change, the default scope from `Post` was not applied, - so we'd get comments on unpublished posts. - - *Jon Leighton* - -* Remove `activerecord-deprecated_finders` as a dependency. - - *Łukasz Strzałkowski* - -* Remove Oracle / Sqlserver / Firebird database tasks that were deprecated in 4.0. - - *kennyj* - -* `find_each` now returns an `Enumerator` when called without a block, so that it - can be chained with other `Enumerable` methods. - - *Ben Woosley* - -* `ActiveRecord::Result.each` now returns an `Enumerator` when called without - a block, so that it can be chained with other `Enumerable` methods. - - *Ben Woosley* - -* Flatten merged join_values before building the joins. - - While joining_values special treatment is given to string values. - By flattening the array it ensures that string values are detected - as strings and not arrays. - - Fixes #10669. - - *Neeraj Singh and iwiznia* - -* Do not load all child records for inverse case. - - currently `post.comments.find(Comment.first.id)` would load all - comments for the given post to set the inverse association. - - This has a huge performance penalty. Because if post has 100k - records and all these 100k records would be loaded in memory - even though the comment id was supplied. - - Fix is to use in-memory records only if loaded? is true. Otherwise - load the records using full sql. - - Fixes #10509. - - *Neeraj Singh* - -* `inspect` on Active Record model classes does not initiate a - new connection. This means that calling `inspect`, when the - database is missing, will no longer raise an exception. - Fixes #10936. - - Example: - - Author.inspect # => "Author(no database connection)" - - *Yves Senn* - -* Handle single quotes in PostgreSQL default column values. - Fixes #10881. - - *Dylan Markow* - -* Log the sql that is actually sent to the database. - - If I have a query that produces sql - `WHERE "users"."name" = 'a b'` then in the log all the - whitespace is being squeezed. So the sql that is printed in the - log is `WHERE "users"."name" = 'a b'`. - - Do not squeeze whitespace out of sql queries. Fixes #10982. - - *Neeraj Singh* - -* Fixture setup no longer depends on `ActiveRecord::Base.configurations`. - This is relevant when `ENV["DATABASE_URL"]` is used in place of a `database.yml`. - - *Yves Senn* - -* Fix mysql2 adapter raises the correct exception when executing a query on a - closed connection. - - *Yves Senn* - -* Ambiguous reflections are on :through relationships are no longer supported. - For example, you need to change this: - - class Author < ActiveRecord::Base - has_many :posts - has_many :taggings, through: :posts - end - - class Post < ActiveRecord::Base - has_one :tagging - has_many :taggings - end - - class Tagging < ActiveRecord::Base - end - - To this: - - class Author < ActiveRecord::Base - has_many :posts - has_many :taggings, through: :posts, source: :tagging - end - - class Post < ActiveRecord::Base - has_one :tagging - has_many :taggings - end - - class Tagging < ActiveRecord::Base - end - - *Aaron Patterson* + *Godfrey Chan* -* Remove column restrictions for `count`, let the database raise if the SQL is - invalid. The previous behavior was untested and surprising for the user. - Fixes #5554. +* Coerce strings when reading attributes. + Fixes #10485. Example: - User.select("name, username").count - # Before => SELECT count(*) FROM users - # After => ActiveRecord::StatementInvalid - - # you can still use `count(:all)` to perform a query unrelated to the - # selected columns - User.select("name, username").count(:all) # => SELECT count(*) FROM users + book = Book.new(title: 12345) + book.save! + book.title # => "12345" *Yves Senn* -* Rails now automatically detects inverse associations. If you do not set the - `:inverse_of` option on the association, then Active Record will guess the - inverse association based on heuristics. - - Note that automatic inverse detection only works on `has_many`, `has_one`, - and `belongs_to` associations. Extra options on the associations will - also prevent the association's inverse from being found automatically. +* Deprecate half-baked support for PostgreSQL range values with excluding beginnings. + We currently map PostgreSQL ranges to Ruby ranges. This conversion is not fully + possible because the Ruby range does not support excluded beginnings. - The automatic guessing of the inverse association uses a heuristic based - on the name of the class, so it may not work for all associations, - especially the ones with non-standard names. - - You can turn off the automatic detection of inverse associations by setting - the `:inverse_of` option to `false` like so: - - class Taggable < ActiveRecord::Base - belongs_to :tag, inverse_of: false - end - - *John Wang* - -* Fix `add_column` with `array` option when using PostgreSQL. Fixes #10432. - - *Adam Anderson* - -* Usage of `implicit_readonly` is being removed`. Please use `readonly` method - explicitly to mark records as `readonly. - Fixes #10615. - - Example: - - user = User.joins(:todos).select("users.*, todos.title as todos_title").readonly(true).first - user.todos_title = 'clean pet' - user.save! # will raise error + The current solution of incrementing the beginning is not correct and is now + deprecated. For subtypes where we don't know how to increment (e.g. `#succ` + is not defined) it will raise an ArgumentException for ranges with excluding + beginnings. *Yves Senn* -* Fix the `:primary_key` option for `has_many` associations. - - Fixes #10693. +* Support for user created range types in PostgreSQL. *Yves Senn* -* Fix bug where tiny types are incorrectly coerced as boolean when the length is more than 1. - - Fixes #10620. - - *Aaron Patterson* - -* Also support extensions in PostgreSQL 9.1. This feature has been supported since 9.1. - - *kennyj* - -* Deprecate `ConnectionAdapters::SchemaStatements#distinct`, - as it is no longer used by internals. - - *Ben Woosley* - -* Fix pending migrations error when loading schema and `ActiveRecord::Base.table_name_prefix` - is not blank. - - Call `assume_migrated_upto_version` on connection to prevent it from first - being picked up in `method_missing`. - - In the base class, `Migration`, `method_missing` expects the argument to be a - table name, and calls `proper_table_name` on the arguments before sending to - `connection`. If `table_name_prefix` or `table_name_suffix` is used, the schema - version changes to `prefix_version_suffix`, breaking `rake test:prepare`. - - Fixes #10411. - - *Kyle Stevens* - -* Method `read_attribute_before_type_cast` should accept input as symbol. - - *Neeraj Singh* - -* Confirm a record has not already been destroyed before decrementing counter cache. - - *Ben Tucker* - -* Fixed a bug in `ActiveRecord#sanitize_sql_hash_for_conditions` in which - `self.class` is an argument to `PredicateBuilder#build_from_hash` - causing `PredicateBuilder` to call non-existent method - `Class#reflect_on_association`. - - *Zach Ohlgren* - -* While removing index if column option is missing then raise IrreversibleMigration exception. - - Following code should raise `IrreversibleMigration`. But the code was - failing since options is an array and not a hash. - - def change - change_table :users do |t| - t.remove_index [:name, :email] - end - end - - Fix was to check if the options is a Hash before operating on it. - - Fixes #10419. - - *Neeraj Singh* - -* Do not overwrite manually built records during one-to-one nested attribute assignment - - For one-to-one nested associations, if you build the new (in-memory) - child object yourself before assignment, then the NestedAttributes - module will not overwrite it, e.g.: - - class Member < ActiveRecord::Base - has_one :avatar - accepts_nested_attributes_for :avatar - - def avatar - super || build_avatar(width: 200) - end - end - - member = Member.new - member.avatar_attributes = {icon: 'sad'} - member.avatar.width # => 200 - - *Olek Janiszewski* - -* fixes bug introduced by #3329. Now, when autosaving associations, - deletions happen before inserts and saves. This prevents a 'duplicate - unique value' database error that would occur if a record being created had - the same value on a unique indexed field as that of a record being destroyed. - - *Johnny Holton* - -* Handle aliased attributes in ActiveRecord::Relation. - - When using symbol keys, ActiveRecord will now translate aliased attribute names to the actual column name used in the database: - - With the model - - class Topic - alias_attribute :heading, :title - end - - The call - - Topic.where(heading: 'The First Topic') - - should yield the same result as - - Topic.where(title: 'The First Topic') - - This also applies to ActiveRecord::Relation::Calculations calls such as `Model.sum(:aliased)` and `Model.pluck(:aliased)`. - - This will not work with SQL fragment strings like `Model.sum('DISTINCT aliased')`. - - *Godfrey Chan* - -* Mute `psql` output when running rake db:schema:load. - - *Godfrey Chan* - -* Trigger a save on `has_one association=(associate)` when the associate contents have changed. - - Fixes #8856. - - *Chris Thompson* - -* Abort a rake task when missing db/structure.sql like `db:schema:load` task. - - *kennyj* - -* rake:db:test:prepare falls back to original environment after execution. - - *Slava Markevich* - -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activerecord/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 142d21ce92573..53f75912261cc 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1036,6 +1036,9 @@ module ClassMethods # Specifies a one-to-many association. The following methods for retrieval and query of # collections of associated objects will be added: # + # +collection+ is a placeholder for the symbol passed as the first argument, so + # has_many :clients would add among others clients.empty?. + # # [collection(force_reload = false)] # Returns an array of all the associated objects. # An empty array is returned if none are found. @@ -1094,9 +1097,6 @@ module ClassMethods # Does the same as collection.create, but raises ActiveRecord::RecordInvalid # if the record is invalid. # - # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so - # has_many :clients would add among others clients.empty?.) - # # === Example # # A Firm class declares has_many :clients, which will add: @@ -1211,6 +1211,9 @@ def has_many(name, scope = nil, options = {}, &extension) # # The following methods for retrieval and query of a single associated object will be added: # + # +association+ is a placeholder for the symbol passed as the first argument, so + # has_one :manager would add among others manager.nil?. + # # [association(force_reload = false)] # Returns the associated object. +nil+ is returned if none is found. # [association=(associate)] @@ -1229,9 +1232,6 @@ def has_many(name, scope = nil, options = {}, &extension) # Does the same as create_association, but raises ActiveRecord::RecordInvalid # if the record is invalid. # - # (+association+ is replaced with the symbol passed as the first argument, so - # has_one :manager would add among others manager.nil?.) - # # === Example # # An Account class declares has_one :beneficiary, which will add: @@ -1317,6 +1317,9 @@ def has_one(name, scope = nil, options = {}) # Methods will be added for retrieval and query for a single associated object, for which # this object holds an id: # + # +association+ is a placeholder for the symbol passed as the first argument, so + # belongs_to :author would add among others author.nil?. + # # [association(force_reload = false)] # Returns the associated object. +nil+ is returned if none is found. # [association=(associate)] @@ -1332,9 +1335,6 @@ def has_one(name, scope = nil, options = {}) # Does the same as create_association, but raises ActiveRecord::RecordInvalid # if the record is invalid. # - # (+association+ is replaced with the symbol passed as the first argument, so - # belongs_to :author would add among others author.nil?.) - # # === Example # # A Post class declares belongs_to :author, which will add: @@ -1454,6 +1454,9 @@ def belongs_to(name, scope = nil, options = {}) # # Adds the following methods for retrieval and query: # + # +collection+ is a placeholder for the symbol passed as the first argument, so + # has_and_belongs_to_many :categories would add among others categories.empty?. + # # [collection(force_reload = false)] # Returns an array of all the associated objects. # An empty array is returned if none are found. @@ -1495,9 +1498,6 @@ def belongs_to(name, scope = nil, options = {}) # with +attributes+, linked to this object through the join table, and that has already been # saved (if it passed the validation). # - # (+collection+ is replaced with the symbol passed as the first argument, so - # has_and_belongs_to_many :categories would add among others categories.empty?.) - # # === Example # # A Developer class declares has_and_belongs_to_many :projects, which will add: diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 4e462568626bf..9ad2d2fb12ccd 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -232,7 +232,7 @@ def invertible_for?(record) # Returns true if record contains the foreign_key def foreign_key_for?(record) - record.attributes.has_key? reflection.foreign_key + record.has_attribute?(reflection.foreign_key) end # This should be implemented to return the values of the relevant key(s) on the owner, diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 03ca00fa709a3..270871c866fdc 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -24,10 +24,6 @@ module Associations # If you need to work on all current children, new and existing records, # +load_target+ and the +loaded+ flag are your friends. class CollectionAssociation < Association #:nodoc: - def initialize(owner, reflection) - super - @proxy = CollectionProxy.create(klass, self) - end # Implements the reader method, e.g. foo.items for Foo.has_many :items def reader(force_reload = false) @@ -37,7 +33,7 @@ def reader(force_reload = false) reload end - @proxy + @proxy ||= CollectionProxy.create(klass, self) end # Implements the writer method, e.g. foo.items= for Foo.has_many :items @@ -517,13 +513,13 @@ def replace_records(new_target, original_target) target end - def concat_records(records) + def concat_records(records, should_raise = false) result = true records.flatten.each do |record| raise_on_type_mismatch!(record) add_to_target(record) do |rec| - result &&= insert_record(rec) unless owner.new_record? + result &&= insert_record(rec, true, should_raise) unless owner.new_record? end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 31b8d27892f22..64bc98c642248 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -30,7 +30,6 @@ def concat(*records) unless owner.new_record? records.flatten.each do |record| raise_on_type_mismatch!(record) - record.save! if record.new_record? end end @@ -40,7 +39,7 @@ def concat(*records) def concat_records(records) ensure_not_nested - records = super + records = super(records, true) if owner.new_record? && records records.flatten.each do |record| diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 9326c9c11707d..57ceec2fc50b8 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -105,8 +105,9 @@ def instance_method_already_implemented?(method_name) super else # If B < A and A defines its own attribute method, then we don't want to overwrite that. - defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods) - defined && !ActiveRecord::Base.method_defined?(method_name) || super + defined = method_defined_within?(method_name, superclass) && + superclass.instance_method(method_name).owner != superclass.generated_attribute_methods + defined || super end end diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 4f58d06f3539e..e9622ca0c15c0 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -301,7 +301,7 @@ def validate_collection_association(reflection) def association_valid?(reflection, record) return true if record.destroyed? || record.marked_for_destruction? - unless valid = record.valid?(self.validation_context) + unless valid = record.valid? if reflection.options[:autosave] record.errors.each do |attribute, message| attribute = "#{reflection.name}.#{attribute}" diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 759e162e1956a..fd185de589baf 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -538,7 +538,10 @@ def clear_all_connections! # for (not necessarily the current class). def retrieve_connection(klass) #:nodoc: pool = retrieve_connection_pool(klass) - (pool && pool.connection) or raise ConnectionNotEstablished + raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool + conn = pool.connection + raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn + conn end # Returns true if a connection that's accessible to this class has 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 ad069f5e53d2f..7f530ec5e472b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -40,7 +40,7 @@ def table_exists?(table_name) # index_exists?(:suppliers, :company_id, unique: true) # # # Check an index with a custom name exists - # index_exists?(:suppliers, :company_id, name: "idx_company_id" + # index_exists?(:suppliers, :company_id, name: "idx_company_id") # def index_exists?(table_name, column_name, options = {}) column_names = Array(column_name) diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index f2fbd5a8f27ae..187eefb9e494f 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -87,7 +87,7 @@ def type_cast_for_write(value) end end - # Casts value (which is a String) to an appropriate instance. + # Casts value to an appropriate instance. def type_cast(value) return nil if value.nil? return coder.load(value) if encoded? @@ -95,7 +95,13 @@ def type_cast(value) klass = self.class case type - when :string, :text then value + when :string, :text + case value + when TrueClass; "1" + when FalseClass; "0" + else + value.to_s + end when :integer then klass.value_to_integer(value) when :float then value.to_f when :decimal then klass.value_to_decimal(value) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index fae260a92100c..e7df073627bca 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -6,6 +6,10 @@ class PostgreSQLAdapter < AbstractAdapter module OID class Type def type; end + + def infinity(options = {}) + ::Float::INFINITY * (options[:negative] ? -1 : 1) + end end class Identity < Type @@ -14,6 +18,14 @@ def type_cast(value) end end + class Text < Type + def type_cast(value) + return if value.nil? + + value.to_s + end + end + class Bit < Type def type_cast(value) if String === value @@ -109,23 +121,19 @@ def initialize(subtype) def extract_bounds(value) from, to = value[1..-2].split(',') { - from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from, - to: (value[-2] == ',' || to == 'infinity') ? infinity : to, + from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from, + to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to, exclude_start: (value[0] == '('), exclude_end: (value[-1] == ')') } end - def infinity(options = {}) - ::Float::INFINITY * (options[:negative] ? -1 : 1) - end - def infinity?(value) value.respond_to?(:infinite?) && value.infinite? end - def to_integer(value) - infinity?(value) ? value : value.to_i + def type_cast_single(value) + infinity?(value) ? value : @subtype.type_cast(value) end def type_cast(value) @@ -133,27 +141,20 @@ def type_cast(value) return value if value.is_a?(::Range) extracted = extract_bounds(value) - - case @subtype - when :date - from = ConnectionAdapters::Column.value_to_date(extracted[:from]) - from -= 1.day if extracted[:exclude_start] - to = ConnectionAdapters::Column.value_to_date(extracted[:to]) - when :decimal - from = BigDecimal.new(extracted[:from].to_s) - # FIXME: add exclude start for ::Range, same for timestamp ranges - to = BigDecimal.new(extracted[:to].to_s) - when :time - from = ConnectionAdapters::Column.string_to_time(extracted[:from]) - to = ConnectionAdapters::Column.string_to_time(extracted[:to]) - when :integer - from = to_integer(extracted[:from]) rescue value ? 1 : 0 - from -= 1 if extracted[:exclude_start] - to = to_integer(extracted[:to]) rescue value ? 1 : 0 - else - return value + from = type_cast_single extracted[:from] + to = type_cast_single extracted[:to] + + if !infinity?(from) && extracted[:exclude_start] + if from.respond_to?(:succ) + from = from.succ + ActiveSupport::Deprecation.warn <<-MESSAGE +Excluding the beginning of a Range is only partialy supported through `#succ`. +This is not reliable and will be removed in the future. + MESSAGE + else + raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')" + end end - ::Range.new(from, to, extracted[:exclude_end]) end end @@ -222,6 +223,10 @@ def type_cast(value) ConnectionAdapters::Column.value_to_decimal value end + + def infinity(options = {}) + BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1) + end end class Hstore < Type @@ -331,15 +336,8 @@ def self.registered_type?(name) alias_type 'int8', 'int2' alias_type 'oid', 'int2' - register_type 'daterange', OID::Range.new(:date) - register_type 'numrange', OID::Range.new(:decimal) - register_type 'tsrange', OID::Range.new(:time) - register_type 'int4range', OID::Range.new(:integer) - alias_type 'tstzrange', 'tsrange' - alias_type 'int8range', 'int4range' - register_type 'numeric', OID::Decimal.new - register_type 'text', OID::Identity.new + register_type 'text', OID::Text.new alias_type 'varchar', 'text' alias_type 'char', 'text' alias_type 'bpchar', 'text' @@ -365,13 +363,13 @@ def self.registered_type?(name) register_type 'date', OID::Date.new register_type 'time', OID::Time.new - register_type 'path', OID::Identity.new + register_type 'path', OID::Text.new register_type 'point', OID::Point.new - register_type 'polygon', OID::Identity.new - register_type 'circle', OID::Identity.new + register_type 'polygon', OID::Text.new + register_type 'circle', OID::Text.new register_type 'hstore', OID::Hstore.new register_type 'json', OID::Json.new - register_type 'ltree', OID::Identity.new + register_type 'ltree', OID::Text.new register_type 'cidr', OID::Cidr.new alias_type 'inet', 'cidr' diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 36c7462419844..d1fb132b604ea 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -785,18 +785,29 @@ def add_oid(row, records_by_oid, type_map) end def initialize_type_map(type_map) - result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA') - leaves, nodes = result.partition { |row| row['typelem'] == '0' } + if supports_ranges? + result = execute(<<-SQL, 'SCHEMA') + SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype + FROM pg_type as t + LEFT JOIN pg_range as r ON oid = rngtypid + SQL + else + result = execute(<<-SQL, 'SCHEMA') + SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput + FROM pg_type as t + SQL + end + ranges, nodes = result.partition { |row| row['typinput'] == 'range_in' } + leaves, nodes = nodes.partition { |row| row['typelem'] == '0' } + arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } - # populate the leaf nodes + # populate the base types leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row| type_map[row['oid'].to_i] = OID::NAMES[row['typname']] end records_by_oid = result.group_by { |row| row['oid'] } - arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } - # populate composite types nodes.each do |row| add_oid row, records_by_oid, type_map @@ -807,6 +818,13 @@ def initialize_type_map(type_map) array = OID::Array.new type_map[row['typelem'].to_i] type_map[row['oid'].to_i] = array end + + # populate range types + ranges.find_all { |row| type_map.key? row['rngsubtype'].to_i }.each do |row| + subtype = type_map[row['rngsubtype'].to_i] + range = OID::Range.new type_map[row['rngsubtype'].to_i] + type_map[row['oid'].to_i] = range + end end FEATURE_NOT_SUPPORTED = "0A000" #:nodoc: diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 11f6a4715861e..4ba4e097778e4 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -93,16 +93,12 @@ def raw_merged_into_default # the connection URL. This hash responds to any string key with # resolved connection information. def default_url_hash - if @raw_config.blank? - Hash.new do |hash, key| - hash[key] = if key.is_a? String - ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(@url).to_hash - else - nil - end + Hash.new do |hash, key| + hash[key] = if key.is_a? String + ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(@url).to_hash + else + nil end - else - {} end end end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 297792aeec099..59467636d7e5a 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -549,7 +549,7 @@ def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {} end # Returns a consistent, platform-independent identifier for +label+. - # Identifiers are positive integers less than 2^32. + # Identifiers are positive integers less than 2^30. def self.identify(label) Zlib.crc32(label.to_s) % MAX_ID end diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 7795561e5163f..5eb7f7e205b4d 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -1,7 +1,7 @@ module ActiveRecord # Returns the version of the currently loaded ActiveRecord as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta2" + Gem::Version.new "4.2.0.alpha" end module VERSION #:nodoc: diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index 5c3a797c414e9..158bc6ee89fe6 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -27,9 +27,6 @@ class PostgresqlOid < ActiveRecord::Base class PostgresqlTimestampWithZone < ActiveRecord::Base end -class PostgresqlUUID < ActiveRecord::Base -end - class PostgresqlLtree < ActiveRecord::Base end @@ -68,14 +65,11 @@ def setup @first_oid = PostgresqlOid.find(1) @connection.execute("INSERT INTO postgresql_timestamp_with_zones (id, time) VALUES (1, '2010-01-01 10:00:00-1')") - - @connection.execute("INSERT INTO postgresql_uuids (id, guid, compact_guid) VALUES(1, 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', 'f06c715096c1012f131764ce8f32c6d8')") - @first_uuid = PostgresqlUUID.find(1) end def teardown [PostgresqlArray, PostgresqlTsvector, PostgresqlMoney, PostgresqlNumber, PostgresqlTime, PostgresqlNetworkAddress, - PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone, PostgresqlUUID].each(&:delete_all) + PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone].each(&:delete_all) end def test_array_escaping @@ -124,10 +118,6 @@ def test_data_type_of_oid_types assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type end - def test_data_type_of_uuid_types - assert_equal :uuid, @first_uuid.column_for_attribute(:guid).type - end - def test_array_values assert_equal [35000,21000,18000,17000], @first_array.commission_by_quarter assert_equal ['foo','bar','baz'], @first_array.nicknames @@ -180,11 +170,6 @@ def test_network_address_values_ipaddr assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address end - def test_uuid_values - assert_equal 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', @first_uuid.guid - assert_equal 'f06c7150-96c1-012f-1317-64ce8f32c6d8', @first_uuid.compact_guid - end - def test_bit_string_values assert_equal '00010101', @first_bit_string.bit_string assert_equal '00010101', @first_bit_string.bit_string_varying diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb new file mode 100644 index 0000000000000..bf8dbd6c3f47e --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +require "cases/helper" +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +class PostgresqlEnumTest < ActiveRecord::TestCase + class PostgresqlEnum < ActiveRecord::Base + self.table_name = "postgresql_enums" + end + + def teardown + @connection.execute 'DROP TABLE IF EXISTS postgresql_enums' + @connection.execute 'DROP TYPE IF EXISTS mood' + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.execute <<-SQL + CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); + SQL + @connection.create_table('postgresql_enums') do |t| + t.column :current_mood, :mood + end + end + end + + def test_enum_mapping + @connection.execute "INSERT INTO postgresql_enums VALUES (1, 'sad');" + enum = PostgresqlEnum.first + assert_equal "sad", enum.current_mood + + enum.current_mood = "happy" + enum.save! + + assert_equal "happy", enum.reload.current_mood + end +end diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index 5c2d8e1c1d694..306bb09a0d4da 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -10,12 +10,22 @@ class PostgresqlRange < ActiveRecord::Base class PostgresqlRangeTest < ActiveRecord::TestCase def teardown @connection.execute 'DROP TABLE IF EXISTS postgresql_ranges' + @connection.execute 'DROP TYPE IF EXISTS floatrange' end def setup - @connection = ActiveRecord::Base.connection + @connection = PostgresqlRange.connection begin @connection.transaction do + @connection.execute 'DROP TABLE IF EXISTS postgresql_ranges' + @connection.execute 'DROP TYPE IF EXISTS floatrange' + @connection.execute <<_SQL + CREATE TYPE floatrange AS RANGE ( + subtype = float8, + subtype_diff = float8mi + ); +_SQL + @connection.create_table('postgresql_ranges') do |t| t.daterange :date_range t.numrange :num_range @@ -24,7 +34,11 @@ def setup t.int4range :int4_range t.int8range :int8_range end + + @connection.add_column 'postgresql_ranges', 'float_range', 'floatrange' end + @connection.send :reload_type_map + PostgresqlRange.reset_column_information rescue ActiveRecord::StatementInvalid skip "do not test on PG without range" end @@ -35,23 +49,26 @@ def setup ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'']", tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']", int4_range: "[1, 10]", - int8_range: "[10, 100]") + int8_range: "[10, 100]", + float_range: "[0.5, 0.7]") insert_range(id: 102, - date_range: "(''2012-01-02'', ''2012-01-04'')", + date_range: "[''2012-01-02'', ''2012-01-04'')", num_range: "[0.1, 0.2)", ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'')", tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')", - int4_range: "(1, 10)", - int8_range: "(10, 100)") + int4_range: "[1, 10)", + int8_range: "[10, 100)", + float_range: "[0.5, 0.7)") insert_range(id: 103, - date_range: "(''2012-01-02'',]", + date_range: "[''2012-01-02'',]", num_range: "[0.1,]", ts_range: "[''2010-01-01 14:30'',]", tstz_range: "[''2010-01-01 14:30:00+05'',]", - int4_range: "(1,]", - int8_range: "(10,]") + int4_range: "[1,]", + int8_range: "[10,]", + float_range: "[0.5,]") insert_range(id: 104, date_range: "[,]", @@ -59,15 +76,17 @@ def setup ts_range: "[,]", tstz_range: "[,]", int4_range: "[,]", - int8_range: "[,]") + int8_range: "[,]", + float_range: "[,]") insert_range(id: 105, - date_range: "(''2012-01-02'', ''2012-01-02'')", - num_range: "(0.1, 0.1)", - ts_range: "(''2010-01-01 14:30'', ''2010-01-01 14:30'')", - tstz_range: "(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", - int4_range: "(1, 1)", - int8_range: "(10, 10)") + date_range: "[''2012-01-02'', ''2012-01-02'')", + num_range: "[0.1, 0.1)", + ts_range: "[''2010-01-01 14:30'', ''2010-01-01 14:30'')", + tstz_range: "[''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", + int4_range: "[1, 1)", + int8_range: "[10, 10)", + float_range: "[0.5, 0.5)") @new_range = PostgresqlRange.new @first_range = PostgresqlRange.find(101) @@ -88,24 +107,24 @@ def test_data_type_of_range_types def test_int4range_values assert_equal 1...11, @first_range.int4_range - assert_equal 2...10, @second_range.int4_range - assert_equal 2...Float::INFINITY, @third_range.int4_range + assert_equal 1...10, @second_range.int4_range + assert_equal 1...Float::INFINITY, @third_range.int4_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int4_range) assert_nil @empty_range.int4_range end def test_int8range_values assert_equal 10...101, @first_range.int8_range - assert_equal 11...100, @second_range.int8_range - assert_equal 11...Float::INFINITY, @third_range.int8_range + assert_equal 10...100, @second_range.int8_range + assert_equal 10...Float::INFINITY, @third_range.int8_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int8_range) assert_nil @empty_range.int8_range end def test_daterange_values assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 5), @first_range.date_range - assert_equal Date.new(2012, 1, 3)...Date.new(2012, 1, 4), @second_range.date_range - assert_equal Date.new(2012, 1, 3)...Float::INFINITY, @third_range.date_range + assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 4), @second_range.date_range + assert_equal Date.new(2012, 1, 2)...Float::INFINITY, @third_range.date_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.date_range) assert_nil @empty_range.date_range end @@ -133,6 +152,14 @@ def test_tstzrange_values assert_nil @empty_range.tstz_range end + def test_custom_range_values + assert_equal 0.5..0.7, @first_range.float_range + assert_equal 0.5...0.7, @second_range.float_range + assert_equal 0.5...Float::INFINITY, @third_range.float_range + assert_equal -Float::INFINITY...Float::INFINITY, @fourth_range.float_range + assert_nil @empty_range.float_range + end + def test_create_tstzrange tstzrange = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2011-02-02 14:30:00 CDT') round_trip(@new_range, :tstz_range, tstzrange) @@ -203,6 +230,38 @@ def test_update_int8range assert_nil_round_trip(@first_range, :int8_range, 39999...39999) end + def test_exclude_beginning_for_subtypes_with_succ_method_is_deprecated + tz = ::ActiveRecord::Base.default_timezone + + silence_warnings { + assert_deprecated { + range = PostgresqlRange.create!(date_range: "(''2012-01-02'', ''2012-01-04'']") + assert_equal Date.new(2012, 1, 3)..Date.new(2012, 1, 4), range.date_range + } + assert_deprecated { + range = PostgresqlRange.create!(ts_range: "(''2010-01-01 14:30'', ''2011-01-01 14:30'']") + assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 1)..Time.send(tz, 2011, 1, 1, 14, 30, 0), range.ts_range + } + assert_deprecated { + range = PostgresqlRange.create!(tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']") + assert_equal Time.parse('2010-01-01 09:30:01 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), range.tstz_range + } + assert_deprecated { + range = PostgresqlRange.create!(int4_range: "(1, 10]") + assert_equal 2..10, range.int4_range + } + assert_deprecated { + range = PostgresqlRange.create!(int8_range: "(10, 100]") + assert_equal 11..100, range.int8_range + } + } + end + + def test_exclude_beginning_for_subtypes_without_succ_method_is_not_supported + assert_raises(ArgumentError) { PostgresqlRange.create!(num_range: "(0.1, 0.2]") } + assert_raises(ArgumentError) { PostgresqlRange.create!(float_range: "(0.5, 0.7]") } + end + private def assert_equal_round_trip(range, attribute, value) round_trip(range, attribute, value) @@ -229,7 +288,8 @@ def insert_range(values) ts_range, tstz_range, int4_range, - int8_range + int8_range, + float_range ) VALUES ( #{values[:id]}, '#{values[:date_range]}', @@ -237,7 +297,8 @@ def insert_range(values) '#{values[:ts_range]}', '#{values[:tstz_range]}', '#{values[:int4_range]}', - '#{values[:int8_range]}' + '#{values[:int8_range]}', + '#{values[:float_range]}' ) SQL end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 3f5d981444dd2..f581c4cf39d0e 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -4,31 +4,77 @@ require 'active_record/base' require 'active_record/connection_adapters/postgresql_adapter' +module PostgresqlUUIDHelper + def connection + @connection ||= ActiveRecord::Base.connection + end + + def enable_uuid_ossp + unless connection.extension_enabled?('uuid-ossp') + connection.enable_extension 'uuid-ossp' + connection.commit_db_transaction + end + + connection.reconnect! + end + + def drop_table(name) + connection.execute "drop table if exists #{name}" + end +end + class PostgresqlUUIDTest < ActiveRecord::TestCase - class UUID < ActiveRecord::Base - self.table_name = 'pg_uuids' + include PostgresqlUUIDHelper + + class UUIDType < ActiveRecord::Base + self.table_name = "uuid_data_type" end - def setup - @connection = ActiveRecord::Base.connection + setup do + connection.create_table "uuid_data_type" do |t| + t.uuid 'guid' + end + end + + teardown do + drop_table "uuid_data_type" + end + + def test_data_type_of_uuid_types + assert_equal :uuid, UUIDType.columns_hash["guid"].type + end - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction + def test_uuid_formats + ["A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11", + "{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}", + "a0eebc999c0b4ef8bb6d6bb9bd380a11", + "a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11", + "{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}"].each do |valid_uuid| + UUIDType.create(guid: valid_uuid) + uuid = UUIDType.last + assert_equal "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", uuid.guid end + end +end - @connection.reconnect! +class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase + include PostgresqlUUIDHelper - @connection.transaction do - @connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| - t.string 'name' - t.uuid 'other_uuid', default: 'uuid_generate_v4()' - end + class UUID < ActiveRecord::Base + self.table_name = 'pg_uuids' + end + + setup do + enable_uuid_ossp + + connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| + t.string 'name' + t.uuid 'other_uuid', default: 'uuid_generate_v4()' end end - def teardown - @connection.execute 'drop table if exists pg_uuids' + teardown do + drop_table "pg_uuids" end if ActiveRecord::Base.connection.supports_extensions? @@ -49,14 +95,14 @@ def test_auto_create_uuid end def test_pk_and_sequence_for_uuid_primary_key - pk, seq = @connection.pk_and_sequence_for('pg_uuids') + pk, seq = connection.pk_and_sequence_for('pg_uuids') assert_equal 'id', pk assert_equal nil, seq end def test_schema_dumper_for_uuid_primary_key schema = StringIO.new - ActiveRecord::SchemaDumper.dump(@connection, schema) + ActiveRecord::SchemaDumper.dump(connection, schema) assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: "uuid_generate_v1\(\)"/, schema.string) assert_match(/t\.uuid "other_uuid", default: "uuid_generate_v4\(\)"/, schema.string) end @@ -64,34 +110,24 @@ def test_schema_dumper_for_uuid_primary_key end class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase - class UUID < ActiveRecord::Base - self.table_name = 'pg_uuids' - end + include PostgresqlUUIDHelper - def setup - @connection = ActiveRecord::Base.connection - @connection.reconnect! - - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end + setup do + enable_uuid_ossp - @connection.transaction do - @connection.create_table('pg_uuids', id: false) do |t| - t.primary_key :id, :uuid, default: nil - t.string 'name' - end + connection.create_table('pg_uuids', id: false) do |t| + t.primary_key :id, :uuid, default: nil + t.string 'name' end end - def teardown - @connection.execute 'drop table if exists pg_uuids' + teardown do + drop_table "pg_uuids" end if ActiveRecord::Base.connection.supports_extensions? def test_id_allows_default_override_via_nil - col_desc = @connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default + col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first @@ -101,6 +137,8 @@ def test_id_allows_default_override_via_nil end class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase + include PostgresqlUUIDHelper + class UuidPost < ActiveRecord::Base self.table_name = 'pg_uuid_posts' has_many :uuid_comments, inverse_of: :uuid_post @@ -111,30 +149,24 @@ class UuidComment < ActiveRecord::Base belongs_to :uuid_post end - def setup - @connection = ActiveRecord::Base.connection - @connection.reconnect! - - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end + setup do + enable_uuid_ossp - @connection.transaction do - @connection.create_table('pg_uuid_posts', id: :uuid) do |t| + connection.transaction do + connection.create_table('pg_uuid_posts', id: :uuid) do |t| t.string 'title' end - @connection.create_table('pg_uuid_comments', id: :uuid) do |t| + connection.create_table('pg_uuid_comments', id: :uuid) do |t| t.uuid :uuid_post_id, default: 'uuid_generate_v4()' t.string 'content' end end end - def teardown - @connection.transaction do - @connection.execute 'drop table if exists pg_uuid_comments' - @connection.execute 'drop table if exists pg_uuid_posts' + teardown do + connection.transaction do + drop_table "pg_uuid_comments" + drop_table "pg_uuid_posts" end end diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb index 2d0d4541b4114..e555c52281be8 100644 --- a/activerecord/test/cases/associations/callbacks_test.rb +++ b/activerecord/test/cases/associations/callbacks_test.rb @@ -101,6 +101,27 @@ def test_has_and_belongs_to_many_add_callback "after_adding#{david.id}"], ar.developers_log end + def test_has_and_belongs_to_many_before_add_called_before_save + dev = nil + new_dev = nil + klass = Class.new(Project) do + def self.name; Project.name; end + has_and_belongs_to_many :developers_with_callbacks, + :class_name => "Developer", + :before_add => lambda { |o,r| + dev = r + new_dev = r.new_record? + } + end + rec = klass.create! + alice = Developer.new(:name => 'alice') + rec.developers_with_callbacks << alice + assert_equal alice, dev + assert_not_nil new_dev + assert new_dev, "record should not have been saved" + assert_not alice.new_record? + end + def test_has_and_belongs_to_many_after_add_called_after_save ar = projects(:active_record) assert ar.developers_log.empty? diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 321440cab7d0a..a86fb15719f1e 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -22,8 +22,6 @@ require 'models/categorization' require 'models/minivan' require 'models/speedometer' -require 'models/pirate' -require 'models/ship' class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase fixtures :authors, :posts, :comments @@ -1832,12 +1830,4 @@ def test_collection_association_with_private_kernel_method end end end - - test 'has_many_association passes context validation to validate children' do - pirate = FamousPirate.new - pirate.famous_ships << ship = FamousShip.new - assert_equal true, pirate.valid? - assert_equal false, pirate.valid?(:conference) - assert_equal "can't be blank", ship.errors[:name].first - end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 47592f312e0a6..026a7fe635c4f 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -817,6 +817,13 @@ def test_collection_create_with_nonstandard_primary_key_on_belongs_to assert author.named_categories(true).include?(category) end + def test_collection_exists + author = authors(:mary) + category = Category.create!(author_ids: [author.id], name: "Primary") + assert category.authors.exists?(id: author.id) + assert category.reload.authors.exists?(id: author.id) + end + def test_collection_delete_with_nonstandard_primary_key_on_belongs_to author = authors(:mary) category = author.named_categories.create(:name => "Primary") diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 6c581a432fd2d..1a1e442df0dc1 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -555,6 +555,24 @@ def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_els end end + def test_converted_values_are_returned_after_assignment + developer = Developer.new(name: 1337, salary: "50000") + + assert_equal "50000", developer.salary_before_type_cast + assert_equal 1337, developer.name_before_type_cast + + assert_equal 50000, developer.salary + assert_equal "1337", developer.name + + developer.save! + + assert_equal "50000", developer.salary_before_type_cast + assert_equal 1337, developer.name_before_type_cast + + assert_equal 50000, developer.salary + assert_equal "1337", developer.name + end + def test_write_nil_to_time_attributes in_time_zone "Pacific Time (US & Canada)" do record = @target.new @@ -728,19 +746,27 @@ def test_bulk_update_raise_unknown_attribute_errro assert "unknown attribute: hello", error.message end - def test_read_attribute_overwrites_private_method_not_considered_implemented - # simulate a model with a db column that shares its name an inherited - # private method (e.g. Object#system) - # - Object.class_eval do - private - def title; "private!"; end + def test_global_methods_are_overwritten + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'computers' + end + + assert !klass.instance_method_already_implemented?(:system) + computer = klass.new + assert_nil computer.system + end + + def test_global_methods_are_overwritte_when_subclassing + klass = Class.new(ActiveRecord::Base) { self.abstract_class = true } + + subklass = Class.new(klass) do + self.table_name = 'computers' end - assert !@target.instance_method_already_implemented?(:title) - topic = @target.new - assert_nil topic.title - Object.send(:undef_method, :title) # remove test method from object + assert !klass.instance_method_already_implemented?(:system) + assert !subklass.instance_method_already_implemented?(:system) + computer = subklass.new + assert_nil computer.system end def test_instance_method_should_be_defined_on_the_base_class diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index c55dd685a131e..d2f97df0fc6c4 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -21,6 +21,31 @@ require 'models/molecule' class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase + def test_autosave_validation + person = Class.new(ActiveRecord::Base) { + self.table_name = 'people' + validate :should_be_cool, :on => :create + def self.name; 'Person'; end + + private + + def should_be_cool + unless self.first_name == 'cool' + errors.add :first_name, "not cool" + end + end + } + reference = Class.new(ActiveRecord::Base) { + self.table_name = "references" + def self.name; 'Reference'; end + belongs_to :person, autosave: true, class: person + } + + u = person.create!(first_name: 'cool') + u.update_attributes!(first_name: 'nah') # still valid because validation only applies on 'create' + assert reference.create!(person: u).persisted? + end + def test_should_not_add_the_same_callbacks_multiple_times_for_has_one assert_no_difference_when_adding_callbacks_twice_for Pirate, :ship end diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 318cc5a32c589..599e8c762cfcc 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -17,6 +17,14 @@ def teardown ENV["DATABASE_URL"] = @previous_database_url end + def test_environment_does_not_exist_in_config_url_does_exist + ENV['DATABASE_URL'] = "postgres://localhost/foo" + config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } } + actual = klass.new(config).resolve + expect_prod = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" } + assert_equal expect_prod, actual["production"] + end + def test_string_connection config = { "production" => "postgres://localhost/foo" } actual = klass.new(config).resolve @@ -69,21 +77,6 @@ def test_blank_with_database_url assert_equal nil, actual[:test] end - def test_sting_with_database_url - ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" - - config = { "production" => "postgres://localhost/foo" } - actual = klass.new(config).resolve - - expected = { "production" => - { "adapter" => "postgresql", - "database" => "foo", - "host" => "localhost" - } - } - assert_equal expected, actual - end - def test_url_sub_key_with_database_url ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index debacf815cb67..2b1749cf70aae 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -271,16 +271,19 @@ def test_migrate_down_with_table_name_prefix ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = '' end - def test_migrate_revert_add_index_with_name - RevertNamedIndexMigration1.new.migrate(:up) - RevertNamedIndexMigration2.new.migrate(:up) - RevertNamedIndexMigration2.new.migrate(:down) - - connection = ActiveRecord::Base.connection - assert connection.index_exists?(:horses, :content), - "index on content should exist" - assert !connection.index_exists?(:horses, :content, name: "horses_index_named"), - "horses_index_named index should not exist" + # MySQL 5.7 and Oracle do not allow to create duplicate indexes on the same columns + unless current_adapter?(:MysqlAdapter, :Mysql2Adapter, :OracleAdapter) + def test_migrate_revert_add_index_with_name + RevertNamedIndexMigration1.new.migrate(:up) + RevertNamedIndexMigration2.new.migrate(:up) + RevertNamedIndexMigration2.new.migrate(:down) + + connection = ActiveRecord::Base.connection + assert connection.index_exists?(:horses, :content), + "index on content should exist" + assert !connection.index_exists?(:horses, :content, name: "horses_index_named"), + "horses_index_named index should not exist" + end end end diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 170e9a49eb7df..9a4d8c6740070 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -395,4 +395,22 @@ def test_default_scope_is_threadsafe threads.each(&:join) end end + + test "additional conditions are ANDed with the default scope" do + scope = DeveloperCalledJamis.where(name: "David") + assert_equal 2, scope.where_values.length + assert_equal [], scope.to_a + end + + test "additional conditions in a scope are ANDed with the default scope" do + scope = DeveloperCalledJamis.david + assert_equal 2, scope.where_values.length + assert_equal [], scope.to_a + end + + test "a scope can remove the condition from the default scope" do + scope = DeveloperCalledJamis.david2 + assert_equal 1, scope.where_values.length + assert_equal Developer.where(name: "David").map(&:id), scope.map(&:id) + end end diff --git a/activerecord/test/fixtures/computers.yml b/activerecord/test/fixtures/computers.yml index daf969d7da9b7..7281a4d76857e 100644 --- a/activerecord/test/fixtures/computers.yml +++ b/activerecord/test/fixtures/computers.yml @@ -1,4 +1,5 @@ workstation: id: 1 + system: 'Linux' developer: 1 extendedWarranty: 1 diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 2e2d8a0d37995..762259ffa3a6a 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -165,6 +165,8 @@ class DeveloperCalledJamis < ActiveRecord::Base default_scope { where(:name => 'Jamis') } scope :poor, -> { where('salary < 150000') } + scope :david, -> { where name: "David" } + scope :david2, -> { unscoped.where name: "David" } end class PoorDeveloperCalledJamis < ActiveRecord::Base diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index 8510c596a7236..7bb0caf44b842 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -85,11 +85,3 @@ def log(record, callback) class DestructivePirate < Pirate has_one :dependent_ship, :class_name => 'Ship', :foreign_key => :pirate_id, :dependent => :destroy end - -class FamousPirate < ActiveRecord::Base - self.table_name = 'pirates' - - has_many :famous_ships - - validates_presence_of :catchphrase, on: :conference -end diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 7a369b9d9a18f..3da031946f0d5 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -17,11 +17,3 @@ def cancel_save_callback_method false end end - -class FamousShip < ActiveRecord::Base - self.table_name = 'ships' - - belongs_to :famous_pirate - - validates_presence_of :name, on: :conference -end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 99a53434f66ae..5f2d6acbcbc52 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -198,6 +198,7 @@ def create_table(*args, &block) end create_table :computers, force: true do |t| + t.string :system t.integer :developer, null: false t.integer :extendedWarranty, null: false end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 713bb3c1e2990..7be22309eaa4b 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,633 +1,29 @@ -* Added `Object#present_in` to simplify value whitelisting. - - Before: - - params[:bucket_type].in?(%w( project calendar )) ? params[:bucket_type] : nil - - After: - - params[:bucket_type].present_in %w( project calendar ) - - *DHH* - -* Time helpers honor the application time zone when passed a date. - - *Xavier Noria* - -* Fix the implementation of Multibyte::Unicode.tidy_bytes for JRuby - - The existing implementation caused JRuby to raise the error: - `Encoding::ConverterNotFoundError: code converter not found (UTF-8 to UTF8-MAC)` - - *Justin Coyne* - -* Fix `to_param` behavior when there are nested empty hashes. - - Before: - - params = {c: 3, d: {}}.to_param # => "&c=3" - - After: - - params = {c: 3, d: {}}.to_param # => "c=3&d=" - - Fixes #13892. - - *Hincu Petru* - -* Deprecate custom `BigDecimal` serialization. - - Deprecate the custom `BigDecimal` serialization that is included when requiring - `active_support/all` as a fix for #12467. Let Ruby handle YAML serialization - for `BigDecimal` instead. - - *David Celis* - -* Fix parsing bugs in `XmlMini` - - Symbols or boolean parsing would raise an error for non string values (e.g. - integers). Decimal parsing would fail due to a missing requirement. - - *Birkir A. Barkarson* - -* Maintain the current timezone when calling `wrap_with_time_zone` - - Extend the solution from the fix for #12163 to the general case where `Time` - methods are wrapped with a time zone. - - Fixes #12596. - - *Andrew White* - -* Remove behavior that automatically remove the Date/Time stubs, added by `travel` - and `travel_to` methods, after each test case. - - Now users have to use the `travel_back` or the block version of `travel` and - `travel_to` methods to clean the stubs. - - *Rafael Mendonça França* - -* Add `travel_back` to remove stubs from `travel` and `travel_to`. - - *Rafael Mendonça França* - -* Remove the deprecation about the `#filter` method. - - Filter objects should now rely on method corresponding to the filter type - (e.g. `#before`). - - *Aaron Patterson* - -* Add `ActiveSupport::JSON::Encoding.time_precision` as a way to configure the - precision of encoded time values: - - Time.utc(2000, 1, 1).as_json # => "2000-01-01T00:00:00.000Z" - ActiveSupport::JSON::Encoding.time_precision = 0 - Time.utc(2000, 1, 1).as_json # => "2000-01-01T00:00:00Z" - - *Parker Selbert* - -* Maintain the current timezone when calling `change` during DST overlap - - Currently if a time is changed during DST overlap in the autumn then the method - `period_for_local` will return the DST period. However if the original time is - not DST then this can be surprising and is not what is generally wanted. This - commit changes that behavior to maintain the current period if it's in the list - of periods returned by `periods_for_local`. - - Fixes #12163. - - *Andrew White* - -* Added `Hash#compact` and `Hash#compact!` for removing items with nil value - from hash. - - *Celestino Gomes* - -* Maintain proleptic gregorian in Time#advance - - `Time#advance` uses `Time#to_date` and `Date#advance` to calculate a new date. - The `Date` object returned by `Time#to_date` is constructed with the assumption - that the `Time` object represents a proleptic gregorian date, but it is - configured to observe the default julian calendar reform date (2299161j) - for purposes of calculating month, date and year: - - Time.new(1582, 10, 4).to_date.to_s # => "1582-09-24" - Time.new(1582, 10, 4).to_date.gregorian.to_s # => "1582-10-04" - - This patch ensures that when the intermediate `Date` object is advanced - to yield a new `Date` object, that the `Time` object for return is constructed - with a proleptic gregorian month, date and year. - - *Riley Lynch* - -* `MemCacheStore` should only accept a `Dalli::Client`, or create one. - - *arthurnn* - -* Don't lazy load the `tzinfo` library as it causes problems on Windows. - - Fixes #13553. - - *Andrew White* - -* Use `remove_possible_method` instead of `remove_method` to avoid - a `NameError` to be thrown on FreeBSD with the `Date` object. - - *Rafael Mendonça França*, *Robin Dupret* - -* `blank?` and `present?` commit to return singletons. - - *Xavier Noria*, *Pavel Pravosud* - -* Fixed Float related error in NumberHelper with large precisions. - - Before: - - ActiveSupport::NumberHelper.number_to_rounded '3.14159', precision: 50 - #=> "3.14158999999999988261834005243144929409027099609375" - - After: - - ActiveSupport::NumberHelper.number_to_rounded '3.14159', precision: 50 - #=> "3.14159000000000000000000000000000000000000000000000" - - *Kenta Murata*, *Akira Matsuda* - -* Default the new `I18n.enforce_available_locales` config to `true`, meaning - `I18n` will make sure that all locales passed to it must be declared in the - `available_locales` list. - - To disable it add the following configuration to your application: - - config.i18n.enforce_available_locales = false - - This also ensures I18n configuration is properly initialized taking the new - option into account, to avoid their deprecations while booting up the app. - - *Carlos Antonio da Silva*, *Yves Senn* - -* Introduce Module#concerning: a natural, low-ceremony way to separate - responsibilities within a class. - - Imported from https://github.com/37signals/concerning#readme - - class Todo < ActiveRecord::Base - concerning :EventTracking do - included do - has_many :events - end - - def latest_event - ... - end - - private - def some_internal_method - ... - end +* Introduce `Concern#class_methods` as a sleek alternative to clunky + `module ClassMethods`. Add `Kernel#concern` to define at the toplevel + without chunky `module Foo; extend ActiveSupport::Concern` boilerplate. + + # app/models/concerns/authentication.rb + concern :Authentication do + included do + after_create :generate_private_key end - concerning :Trashable do - def trashed? - ... - end - - def latest_event - super some_option: true + class_methods do + def authenticate(credentials) + # ... end end - end - - is equivalent to defining these modules inline, extending them into - concerns, then mixing them in to the class. - - Inline concerns tame "junk drawer" classes that intersperse many unrelated - class-level declarations, public instance methods, and private - implementation. Coalesce related bits and give them definition. - These are a stepping stone toward future growth & refactoring. - - When to move on from an inline concern: - * Encapsulating state? Extract collaborator object. - * Encompassing more public behavior or implementation? Move to separate file. - * Sharing behavior among classes? Move to separate file. - - *Jeremy Kemper* - -* Fix file descriptor being leaked on each call to `Kernel.silence_stream`. - - *Mario Visic* - -* Added `Date#all_week/month/quarter/year` for generating date ranges. - - *Dmitriy Meremyanin* - -* Add `Time.zone.yesterday` and `Time.zone.tomorrow`. These follow the - behavior of Ruby's `Date.yesterday` and `Date.tomorrow` but return localized - versions, similar to how `Time.zone.today` has returned a localized version - of `Date.today`. - - *Colin Bartlett* - -* Show valid keys when `assert_valid_keys` raises an exception, and show the - wrong value as it was entered. - - *Gonzalo Rodríguez-Baltanás Díaz* - -* Both `cattr_*` and `mattr_*` method definitions now live in `active_support/core_ext/module/attribute_accessors`. - - Requires to `active_support/core_ext/class/attribute_accessors` are - deprecated and will be removed in Ruby on Rails 4.2. - - *Genadi Samokovarov* - -* Deprecated `Numeric#{ago,until,since,from_now}`, the user is expected to explicitly - convert the value into an AS::Duration, i.e. `5.ago` => `5.seconds.ago` - - This will help to catch subtle bugs like: - - def recent?(days = 3) - self.created_at >= days.ago - end - - The above code would check if the model is created within the last 3 **seconds**. - - In the future, `Numeric#{ago,until,since,from_now}` should be removed completely, - or throw some sort of errors to indicate there are no implicit conversion from - Numeric to AS::Duration. - - *Godfrey Chan* - -* Requires JSON gem version 1.7.7 or above due to a security issue in older versions. - - *Godfrey Chan* - -* Removed the old pure-Ruby JSON encoder and switched to a new encoder based on the built-in JSON - gem. - - Support for encoding `BigDecimal` as a JSON number, as well as defining custom `encode_json` - methods to control the JSON output has been **removed from core**. The new encoder will always - encode BigDecimals as `String`s and ignore any custom `encode_json` methods. - - The old encoder has been extracted into the `activesupport-json_encoder` gem. Installing that - gem will bring back the ability to encode `BigDecimal`s as numbers as well as `encode_json` - support. - - Setting the related configuration `ActiveSupport.encode_big_decimal_as_string` without the - `activesupport-json_encoder` gem installed will raise an error. - - *Godfrey Chan* - -* Add `ActiveSupport::Testing::TimeHelpers#travel` and `#travel_to`. These methods change current - time to the given time or time difference by stubbing `Time.now` and `Date.today` to return the - time or date after the difference calculation, or the time or date that got passed into the - method respectively. - - Example for `#travel`: - - Time.now # => 2013-11-09 15:34:49 -05:00 - travel 1.day - Time.now # => 2013-11-10 15:34:49 -05:00 - Date.today # => Sun, 10 Nov 2013 - - Example for `#travel_to`: - - Time.now # => 2013-11-09 15:34:49 -05:00 - travel_to Time.new(2004, 11, 24, 01, 04, 44) - Time.now # => 2004-11-24 01:04:44 -05:00 - Date.today # => Wed, 24 Nov 2004 - - Both of these methods also accept a block, which will return the current time back to its - original state at the end of the block: - - Time.now # => 2013-11-09 15:34:49 -05:00 - - travel 1.day do - User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00 - end - - travel_to Time.new(2004, 11, 24, 01, 04, 44) do - User.create.created_at # => Wed, 24 Nov 2004 01:04:44 EST -05:00 - end - - Time.now # => 2013-11-09 15:34:49 -05:00 - - This module is included in `ActiveSupport::TestCase` automatically. - - *Prem Sichanugrist*, *DHH* - -* Unify `cattr_*` interface: allow to pass a block to `cattr_reader`. - - Example: - - class A - cattr_reader(:defr) { 'default_reader_value' } - end - A.defr # => 'default_reader_value' - - *Alexey Chernenkov* - -* Improved compatibility with the stdlib JSON gem. - - Previously, calling `::JSON.{generate,dump}` sometimes causes unexpected - failures such as intridea/multi_json#86. - - `::JSON.{generate,dump}` now bypasses the ActiveSupport JSON encoder - completely and yields the same result with or without ActiveSupport. This - means that it will **not** call `as_json` and will ignore any options that - the JSON gem does not natively understand. To invoke ActiveSupport's JSON - encoder instead, use `obj.to_json(options)` or - `ActiveSupport::JSON.encode(obj, options)`. - - *Godfrey Chan* - -* Fix Active Support `Time#to_json` and `DateTime#to_json` to return 3 decimal - places worth of fractional seconds, similar to `TimeWithZone`. - - *Ryan Glover* - -* Removed circular reference protection in JSON encoder, deprecated - `ActiveSupport::JSON::Encoding::CircularReferenceError`. - - *Godfrey Chan*, *Sergio Campamá* - -* Add `capitalize` option to `Inflector.humanize`, so strings can be humanized without being capitalized: - - 'employee_salary'.humanize # => "Employee salary" - 'employee_salary'.humanize(capitalize: false) # => "employee salary" - - *claudiob* - -* Fixed `Object#as_json` and `Struct#as_json` not working properly with options. They now take - the same options as `Hash#as_json`: - - struct = Struct.new(:foo, :bar).new - struct.foo = "hello" - struct.bar = "world" - json = struct.as_json(only: [:foo]) # => {foo: "hello"} - - *Sergio Campamá*, *Godfrey Chan* - -* Added `Numeric#in_milliseconds`, like `1.hour.in_milliseconds`, so we can feed them to JavaScript functions like `getTime()`. - - *DHH* - -* Calling `ActiveSupport::JSON.decode` with unsupported options now raises an error. - - *Godfrey Chan* - -* Support `:unless_exist` in `FileStore`. - - *Michael Grosser* - -* Fix `slice!` deleting the default value of the hash. - - *Antonio Santos* - -* `require_dependency` accepts objects that respond to `to_path`, in - particular `Pathname` instances. - - *Benjamin Fleischer* - -* Disable the ability to iterate over Range of AS::TimeWithZone - due to significant performance issues. - - *Bogdan Gusiev* - -* Allow attaching event subscribers to ActiveSupport::Notifications namespaces - before they're defined. Essentially, this means instead of this: - - class JokeSubscriber < ActiveSupport::Subscriber - def sql(event) - puts "A rabbi and a priest walk into a bar..." - end - # This call needs to happen *after* defining the methods. - attach_to "active_record" - end - - You can do this: - - class JokeSubscriber < ActiveSupport::Subscriber - # This is much easier to read! - attach_to "active_record" - - def sql(event) - puts "A rabbi and a priest walk into a bar..." + def generate_private_key + # ... end end - This should make it easier to read and understand these subscribers. - - *Daniel Schierbeck* - -* Add `Date#middle_of_day`, `DateTime#middle_of_day` and `Time#middle_of_day` methods. - - Also added `midday`, `noon`, `at_midday`, `at_noon` and `at_middle_of_day` as aliases. - - *Anatoli Makarevich* - -* Fix ActiveSupport::Cache::FileStore#cleanup to no longer rely on missing each_key method. - - *Murray Steele* - -* Ensure that autoloaded constants in all-caps nestings are marked as - autoloaded. - - *Simon Coffey* - -* Add `String#remove(pattern)` as a short-hand for the common pattern of - `String#gsub(pattern, '')`. - - *DHH* - -* Adds a new deprecation behaviour that raises an exception. Throwing this - line into +config/environments/development.rb+ - - ActiveSupport::Deprecation.behavior = :raise - - will cause the application to raise an +ActiveSupport::DeprecationException+ - on deprecations. - - Use this for aggressive deprecation cleanups. - - *Xavier Noria* - -* Remove 'cow' => 'kine' irregular inflection from default inflections. - - *Andrew White* - -* Add `DateTime#to_s(:iso8601)` and `Date#to_s(:iso8601)` for consistency. - - *Andrew White* - -* Add `Time#to_s(:iso8601)` for easy conversion of times to the iso8601 format for easy Javascript date parsing. - - *DHH* - -* Improve `ActiveSupport::Cache::MemoryStore` cache size calculation. - The memory used by a key/entry pair is calculated via `#cached_size`: - - def cached_size(key, entry) - key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD - end - - The value of `PER_ENTRY_OVERHEAD` is 240 bytes based on an [empirical - estimation](https://gist.github.com/ssimeonov/6047200) for 64-bit MRI on - 1.9.3 and 2.0. - - Fixes #11512. - - *Simeon Simeonov* - -* Only raise `Module::DelegationError` if it's the source of the exception. - - Fixes #10559. - - *Andrew White* - -* Make `Time.at_with_coercion` retain the second fraction and return local time. - - Fixes #11350. - - *Neer Friedman*, *Andrew White* - -* Make `HashWithIndifferentAccess#select` always return the hash, even when - `Hash#select!` returns `nil`, to allow further chaining. - - *Marc Schütz* - -* Remove deprecated `String#encoding_aware?` core extensions (`core_ext/string/encoding`). - - *Arun Agrawal* - -* Remove deprecated `Module#local_constant_names` in favor of `Module#local_constants`. - - *Arun Agrawal* - -* Remove deprecated `DateTime.local_offset` in favor of `DateTime.civil_from_format`. - - *Arun Agrawal* - -* Remove deprecated `Logger` core extensions (`core_ext/logger.rb`). - - *Carlos Antonio da Silva* - -* Remove deprecated `Time#time_with_datetime_fallback`, `Time#utc_time` - and `Time#local_time` in favor of `Time#utc` and `Time#local`. - - *Vipul A M* - -* Remove deprecated `Hash#diff` with no replacement. - - If you're using it to compare hashes for the purpose of testing, please use - MiniTest's `assert_equal` instead. - - *Carlos Antonio da Silva* - -* Remove deprecated `Date#to_time_in_current_zone` in favor of `Date#in_time_zone`. - - *Vipul A M* - -* Remove deprecated `Proc#bind` with no replacement. - - *Carlos Antonio da Silva* - -* Remove deprecated `Array#uniq_by` and `Array#uniq_by!`, use native - `Array#uniq` and `Array#uniq!` instead. - - *Carlos Antonio da Silva* - -* Remove deprecated `ActiveSupport::BasicObject`, use `ActiveSupport::ProxyObject` instead. - - *Carlos Antonio da Silva* - -* Remove deprecated `BufferedLogger`, use `ActiveSupport::Logger` instead. - - *Yves Senn* - -* Remove deprecated `assert_present` and `assert_blank` methods, use `assert - object.blank?` and `assert object.present?` instead. - - *Yves Senn* - -* Fix return value from `BacktraceCleaner#noise` when the cleaner is configured - with multiple silencers. - - Fixes #11030. - - *Mark J. Titorenko* - -* `HashWithIndifferentAccess#select` now returns a `HashWithIndifferentAccess` - instance instead of a `Hash` instance. - - Fixes #10723. - - *Albert Llop* - -* Add `DateTime#usec` and `DateTime#nsec` so that `ActiveSupport::TimeWithZone` keeps - sub-second resolution when wrapping a `DateTime` value. - - Fixes #10855. - - *Andrew White* - -* Fix `ActiveSupport::Dependencies::Loadable#load_dependency` calling - `#blame_file!` on Exceptions that do not have the Blamable mixin - - *Andrew Kreiling* - -* Override `Time.at` to support the passing of Time-like values when called with a single argument. - - *Andrew White* - -* Prevent side effects to hashes inside arrays when - `Hash#with_indifferent_access` is called. - - Fixes #10526. - - *Yves Senn* - -* Removed deprecated `ActiveSupport::JSON::Variable` with no replacement. - - *Toshinori Kajihara* - -* Raise an error when multiple `included` blocks are defined for a Concern. - The old behavior would silently discard previously defined blocks, running - only the last one. - - *Mike Dillon* - -* Replace `multi_json` with `json`. - - Since Rails requires Ruby 1.9 and since Ruby 1.9 includes `json` in the standard library, - `multi_json` is no longer necessary. - - *Erik Michaels-Ober* - -* Added escaping of U+2028 and U+2029 inside the json encoder. - These characters are legal in JSON but break the Javascript interpreter. - After escaping them, the JSON is still legal and can be parsed by Javascript. - - *Mario Caropreso + Viktor Kelemen + zackham* - -* Fix skipping object callbacks using metadata fetched via callback chain - inspection methods (`_*_callbacks`) - - *Sean Walbran* - -* Add a `fetch_multi` method to the cache stores. The method provides - an easy to use API for fetching multiple values from the cache. - - Example: - - # Calculating scores is expensive, so we only do it for posts - # that have been updated. Cache keys are automatically extracted - # from objects that define a #cache_key method. - scores = Rails.cache.fetch_multi(*posts) do |post| - calculate_score(post) + # app/models/user.rb + class User < ActiveRecord::Base + include Authentication end - *Daniel Schierbeck* + *Jeremy Kemper* -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activesupport/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activesupport/CHANGELOG.md) for previous changes. diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 4eaf57f385349..6afb07bd7253d 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/string/inflections' -require 'rack/body_proxy' module ActiveSupport module Cache @@ -9,6 +8,8 @@ module Strategy # duration of a block. Repeated calls to the cache for the same key will hit the # in-memory cache for faster access. module LocalCache + autoload :Middleware, 'active_support/cache/strategy/local_cache_middleware' + # Class for storing and registering the local caches. class LocalCacheRegistry # :nodoc: extend ActiveSupport::PerThreadRegistry @@ -64,37 +65,6 @@ def delete_entry(key, options) def with_local_cache use_temporary_local_cache(LocalStore.new) { yield } end - - #-- - # This class wraps up local storage for middlewares. Only the middleware method should - # construct them. - class Middleware # :nodoc: - attr_reader :name, :local_cache_key - - def initialize(name, local_cache_key) - @name = name - @local_cache_key = local_cache_key - @app = nil - end - - def new(app) - @app = app - self - end - - def call(env) - LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new) - response = @app.call(env) - response[2] = ::Rack::BodyProxy.new(response[2]) do - LocalCacheRegistry.set_cache_for(local_cache_key, nil) - end - response - rescue Exception - LocalCacheRegistry.set_cache_for(local_cache_key, nil) - raise - end - end - # Middleware class can be inserted as a Rack handler to be local cache for the # duration of request. def middleware diff --git a/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb b/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb new file mode 100644 index 0000000000000..901c2e05a84ea --- /dev/null +++ b/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb @@ -0,0 +1,39 @@ +require 'rack/body_proxy' +module ActiveSupport + module Cache + module Strategy + module LocalCache + + #-- + # This class wraps up local storage for middlewares. Only the middleware method should + # construct them. + class Middleware # :nodoc: + attr_reader :name, :local_cache_key + + def initialize(name, local_cache_key) + @name = name + @local_cache_key = local_cache_key + @app = nil + end + + def new(app) + @app = app + self + end + + def call(env) + LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new) + response = @app.call(env) + response[2] = ::Rack::BodyProxy.new(response[2]) do + LocalCacheRegistry.set_cache_for(local_cache_key, nil) + end + response + rescue Exception + LocalCacheRegistry.set_cache_for(local_cache_key, nil) + raise + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index b796d01dfd39e..9d5cee54e3ef8 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -26,7 +26,7 @@ module ActiveSupport # scope :disabled, -> { where(disabled: true) } # end # - # module ClassMethods + # class_methods do # ... # end # end @@ -130,5 +130,13 @@ def included(base = nil, &block) super end end + + def class_methods(&class_methods_module_definition) + mod = const_defined?(:ClassMethods) ? + const_get(:ClassMethods) : + const_set(:ClassMethods, Module.new) + + mod.module_eval(&class_methods_module_definition) + end end end diff --git a/activesupport/lib/active_support/core_ext/kernel.rb b/activesupport/lib/active_support/core_ext/kernel.rb index 0275f4c037c1f..aa19aed43b4f8 100644 --- a/activesupport/lib/active_support/core_ext/kernel.rb +++ b/activesupport/lib/active_support/core_ext/kernel.rb @@ -1,4 +1,5 @@ -require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/agnostics' +require 'active_support/core_ext/kernel/concern' require 'active_support/core_ext/kernel/debugger' +require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' diff --git a/activesupport/lib/active_support/core_ext/kernel/concern.rb b/activesupport/lib/active_support/core_ext/kernel/concern.rb new file mode 100644 index 0000000000000..c200a78d3623f --- /dev/null +++ b/activesupport/lib/active_support/core_ext/kernel/concern.rb @@ -0,0 +1,10 @@ +require 'active_support/core_ext/module/concerning' + +module Kernel + # A shortcut to define a toplevel concern, not within a module. + # + # See ActiveSupport::CoreExt::Module::Concerning for more. + def concern(topic, &module_definition) + Object.concern topic, &module_definition + end +end diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 3b5e205244fad..f3f8416905dfc 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -41,6 +41,8 @@ def silence_stderr #:nodoc: # end # # puts 'But this will' + # + # This method is not thread-safe. def silence_stream(stream) old_stream = stream.dup stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') @@ -100,6 +102,8 @@ def capture(stream) # Silences both STDOUT and STDERR, even for subprocesses. # # quietly { system 'bundle install' } + # + # This method is not thread-safe. def quietly silence_stream(STDOUT) do silence_stream(STDERR) do diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb index 141f19e7b3e82..55f281b213bad 100644 --- a/activesupport/lib/active_support/core_ext/object/inclusion.rb +++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb @@ -16,12 +16,12 @@ def in?(another_object) # Returns the receiver if it's included in the argument otherwise returns +nil+. # Argument must be any object which responds to +#include?+. Usage: # - # params[:bucket_type].present_in %w( project calendar ) + # params[:bucket_type].presence_in %w( project calendar ) # # This will throw an ArgumentError if the argument doesn't respond to +#include?+. # # @return [Object] - def present_in(another_object) + def presence_in(another_object) self.in?(another_object) ? self : nil end end diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index b9d6417b07cd6..38f726ea34661 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -1,7 +1,7 @@ module ActiveSupport # Returns the version of the currently loaded ActiveSupport as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta2" + Gem::Version.new "4.2.0.alpha" end module VERSION #:nodoc: diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb index a74ee880b2607..60bd8a06aa26b 100644 --- a/activesupport/test/concern_test.rb +++ b/activesupport/test/concern_test.rb @@ -5,7 +5,7 @@ class ConcernTest < ActiveSupport::TestCase module Baz extend ActiveSupport::Concern - module ClassMethods + class_methods do def baz "baz" end @@ -33,6 +33,12 @@ module Bar include Baz + module ClassMethods + def baz + "bar's baz + " + super + end + end + def bar "bar" end @@ -73,7 +79,7 @@ def test_modules_dependencies_are_met @klass.send(:include, Bar) assert_equal "bar", @klass.new.bar assert_equal "bar+baz", @klass.new.baz - assert_equal "baz", @klass.baz + assert_equal "bar's baz + baz", @klass.baz assert @klass.included_modules.include?(ConcernTest::Bar) end diff --git a/activesupport/test/core_ext/kernel/concern_test.rb b/activesupport/test/core_ext/kernel/concern_test.rb new file mode 100644 index 0000000000000..9b1fdda3b0ef0 --- /dev/null +++ b/activesupport/test/core_ext/kernel/concern_test.rb @@ -0,0 +1,12 @@ +require 'abstract_unit' +require 'active_support/core_ext/kernel/concern' + +class KernelConcernTest < ActiveSupport::TestCase + def test_may_be_defined_at_toplevel + mod = ::TOPLEVEL_BINDING.eval 'concern(:ToplevelConcern) { }' + assert_equal mod, ::ToplevelConcern + assert_kind_of ActiveSupport::Concern, ::ToplevelConcern + assert !Object.ancestors.include?(::ToplevelConcern), mod.ancestors.inspect + Object.send :remove_const, :ToplevelConcern + end +end diff --git a/activesupport/test/core_ext/module/concerning_test.rb b/activesupport/test/core_ext/module/concerning_test.rb index c6863b24a4044..07d860b71ce50 100644 --- a/activesupport/test/core_ext/module/concerning_test.rb +++ b/activesupport/test/core_ext/module/concerning_test.rb @@ -1,35 +1,65 @@ require 'abstract_unit' require 'active_support/core_ext/module/concerning' -class ConcerningTest < ActiveSupport::TestCase - def test_concern_shortcut_creates_a_module_but_doesnt_include_it - mod = Module.new { concern(:Foo) { } } - assert_kind_of Module, mod::Foo - assert mod::Foo.respond_to?(:included) - assert !mod.ancestors.include?(mod::Foo), mod.ancestors.inspect +class ModuleConcerningTest < ActiveSupport::TestCase + def test_concerning_declares_a_concern_and_includes_it_immediately + klass = Class.new { concerning(:Foo) { } } + assert klass.ancestors.include?(klass::Foo), klass.ancestors.inspect end +end +class ModuleConcernTest < ActiveSupport::TestCase def test_concern_creates_a_module_extended_with_active_support_concern klass = Class.new do - concern :Foo do + concern :Baz do included { @foo = 1 } def should_be_public; end end end # Declares a concern but doesn't include it - assert_kind_of Module, klass::Foo - assert !klass.ancestors.include?(klass::Foo), klass.ancestors.inspect + assert klass.const_defined?(:Baz, false) + assert !ModuleConcernTest.const_defined?(:Baz) + assert_kind_of ActiveSupport::Concern, klass::Baz + assert !klass.ancestors.include?(klass::Baz), klass.ancestors.inspect # Public method visibility by default - assert klass::Foo.public_instance_methods.map(&:to_s).include?('should_be_public') + assert klass::Baz.public_instance_methods.map(&:to_s).include?('should_be_public') # Calls included hook - assert_equal 1, Class.new { include klass::Foo }.instance_variable_get('@foo') + assert_equal 1, Class.new { include klass::Baz }.instance_variable_get('@foo') end - def test_concerning_declares_a_concern_and_includes_it_immediately - klass = Class.new { concerning(:Foo) { } } - assert klass.ancestors.include?(klass::Foo), klass.ancestors.inspect + class Foo + concerning :Bar do + module ClassMethods + def will_be_orphaned; end + end + + const_set :ClassMethods, Module.new { + def hacked_on; end + } + + # Doesn't overwrite existing ClassMethods module. + class_methods do + def nicer_dsl; end + end + + # Doesn't overwrite previous class_methods definitions. + class_methods do + def doesnt_clobber; end + end + end + end + + def test_using_class_methods_blocks_instead_of_ClassMethods_module + assert !Foo.respond_to?(:will_be_orphaned) + assert Foo.respond_to?(:hacked_on) + assert Foo.respond_to?(:nicer_dsl) + assert Foo.respond_to?(:doesnt_clobber) + + # Orphan in Foo::ClassMethods, not Bar::ClassMethods. + assert Foo.const_defined?(:ClassMethods) + assert Foo::ClassMethods.method_defined?(:will_be_orphaned) end end diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb index c5e2cc693a77b..b054a8dd31ed7 100644 --- a/activesupport/test/core_ext/object/inclusion_test.rb +++ b/activesupport/test/core_ext/object/inclusion_test.rb @@ -48,8 +48,8 @@ def test_no_method_catching assert_raise(ArgumentError) { 1.in?(1) } end - def test_present_in - assert_equal "stuff", "stuff".present_in(%w( lots of stuff )) - assert_nil "stuff".present_in(%w( lots of crap )) + def test_presence_in + assert_equal "stuff", "stuff".presence_in(%w( lots of stuff )) + assert_nil "stuff".presence_in(%w( lots of crap )) end end diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md index 4cfc5b1f10d2b..a7c215c295f56 100644 --- a/guides/CHANGELOG.md +++ b/guides/CHANGELOG.md @@ -1,13 +1 @@ -* Fixed missing line and shadow on service pages(404, 422, 500). - - *Dmitry Korotkov* - -* Removed repetitive th tags. Instead of them added one th tag with a colspan attribute. - - *Sıtkı Bağdat* - -* Added the Rails maintenance policy to the guides. - - *Matias Korhonen* - -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/guides/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/guides/CHANGELOG.md) for previous changes. diff --git a/guides/assets/images/getting_started/article_with_comments.png b/guides/assets/images/getting_started/article_with_comments.png index 1918e9bf2893a..117a78a39f7ca 100644 Binary files a/guides/assets/images/getting_started/article_with_comments.png and b/guides/assets/images/getting_started/article_with_comments.png differ diff --git a/guides/assets/images/getting_started/challenge.png b/guides/assets/images/getting_started/challenge.png index cc12162677818..5b88a842b282c 100644 Binary files a/guides/assets/images/getting_started/challenge.png and b/guides/assets/images/getting_started/challenge.png differ diff --git a/guides/assets/images/getting_started/confirm_dialog.png b/guides/assets/images/getting_started/confirm_dialog.png index e57d4b409e9f5..9755f581a6aff 100644 Binary files a/guides/assets/images/getting_started/confirm_dialog.png and b/guides/assets/images/getting_started/confirm_dialog.png differ diff --git a/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png b/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png index e263f7f8b2914..9f32c684720d8 100644 Binary files a/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png and b/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png differ diff --git a/guides/assets/images/getting_started/form_with_errors.png b/guides/assets/images/getting_started/form_with_errors.png index 04ff8b1e2d461..98bff37d4a30e 100644 Binary files a/guides/assets/images/getting_started/form_with_errors.png and b/guides/assets/images/getting_started/form_with_errors.png differ diff --git a/guides/assets/images/getting_started/index_action_with_edit_link.png b/guides/assets/images/getting_started/index_action_with_edit_link.png index 22f994d993b3c..0566a3ffde0ce 100644 Binary files a/guides/assets/images/getting_started/index_action_with_edit_link.png and b/guides/assets/images/getting_started/index_action_with_edit_link.png differ diff --git a/guides/assets/images/getting_started/new_article.png b/guides/assets/images/getting_started/new_article.png index 89fc0b2605945..bd3ae4fa675f1 100644 Binary files a/guides/assets/images/getting_started/new_article.png and b/guides/assets/images/getting_started/new_article.png differ diff --git a/guides/assets/images/getting_started/rails_welcome.jpg b/guides/assets/images/getting_started/rails_welcome.jpg deleted file mode 100644 index 65a44cdfe5261..0000000000000 Binary files a/guides/assets/images/getting_started/rails_welcome.jpg and /dev/null differ diff --git a/guides/assets/images/getting_started/rails_welcome.png b/guides/assets/images/getting_started/rails_welcome.png new file mode 100644 index 0000000000000..3e07c948a0a47 Binary files /dev/null and b/guides/assets/images/getting_started/rails_welcome.png differ diff --git a/guides/assets/images/getting_started/routing_error_no_controller.png b/guides/assets/images/getting_started/routing_error_no_controller.png index ae83b6a68c35f..ed62862291d44 100644 Binary files a/guides/assets/images/getting_started/routing_error_no_controller.png and b/guides/assets/images/getting_started/routing_error_no_controller.png differ diff --git a/guides/assets/images/getting_started/routing_error_no_route_matches.png b/guides/assets/images/getting_started/routing_error_no_route_matches.png index 1cbddfa0f192b..08c54f921feac 100644 Binary files a/guides/assets/images/getting_started/routing_error_no_route_matches.png and b/guides/assets/images/getting_started/routing_error_no_route_matches.png differ diff --git a/guides/assets/images/getting_started/show_action_for_articles.png b/guides/assets/images/getting_started/show_action_for_articles.png index 9467df6a0780f..4dad704f89ea8 100644 Binary files a/guides/assets/images/getting_started/show_action_for_articles.png and b/guides/assets/images/getting_started/show_action_for_articles.png differ diff --git a/guides/assets/images/getting_started/template_is_missing_articles_new.png b/guides/assets/images/getting_started/template_is_missing_articles_new.png index ba630cfc23137..4e636d09ff4a2 100644 Binary files a/guides/assets/images/getting_started/template_is_missing_articles_new.png and b/guides/assets/images/getting_started/template_is_missing_articles_new.png differ diff --git a/guides/assets/images/getting_started/unknown_action_create_for_articles.png b/guides/assets/images/getting_started/unknown_action_create_for_articles.png index ed89c4f3d7a1d..fd20cd53dc18d 100644 Binary files a/guides/assets/images/getting_started/unknown_action_create_for_articles.png and b/guides/assets/images/getting_started/unknown_action_create_for_articles.png differ diff --git a/guides/assets/images/getting_started/unknown_action_new_for_articles.png b/guides/assets/images/getting_started/unknown_action_new_for_articles.png index e8f2b9a16ad79..e948a51e4a113 100644 Binary files a/guides/assets/images/getting_started/unknown_action_new_for_articles.png and b/guides/assets/images/getting_started/unknown_action_new_for_articles.png differ diff --git a/guides/code/getting_started/Gemfile b/guides/code/getting_started/Gemfile index ecb6e7aa1abb5..c3d7e96c4d289 100644 --- a/guides/code/getting_started/Gemfile +++ b/guides/code/getting_started/Gemfile @@ -27,7 +27,7 @@ gem 'sdoc', '~> 0.4.0', group: :doc gem 'spring', group: :development # Use ActiveModel has_secure_password -# gem 'bcrypt-ruby', '~> 3.1.2' +# gem 'bcrypt', '~> 3.1.7' # Use unicorn as the app server # gem 'unicorn' diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index 27c53689c4ab7..9fd930963a77a 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -15,7 +15,7 @@

<% end %>

- The guides for Rails 3.2.x are available at http://guides.rubyonrails.org/v3.2.16/. + The guides for Rails 3.2.x are available at http://guides.rubyonrails.org/v3.2.17/.

The guides for Rails 2.3.x are available at http://guides.rubyonrails.org/v2.3.11/. diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index 295c471db9b62..261538d0bef5a 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -215,6 +215,13 @@ ordinary method names, symbols, paths (with forward slashes), etc. Please use `...` for everything else, notably class or module names with a namespace as in `ActiveRecord::Base`. +You can quickly test the RDoc output with the following command: + +``` +$ echo "+:to_param+" | rdoc --pipe +#=>

:to_param

+``` + ### Regular Font When "true" and "false" are English words rather than Ruby keywords use a regular font: diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index fa2e57ff924fb..5bb895cb78812 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -302,7 +302,7 @@ Sprockets uses files named `index` (with the relevant extensions) for a special purpose. For example, if you have a jQuery library with many modules, which is stored in -`lib/assets/library_name`, the file `lib/assets/library_name/index.js` serves as +`lib/assets/javascripts/library_name`, the file `lib/assets/javascripts/library_name/index.js` serves as the manifest for all files in this library. This file could include a list of all the required files in order, or a simple `require_tree` directive. diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index a16b9ac8da099..3ef9e04a02de7 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -57,9 +57,9 @@ learned elsewhere, you may have a less happy experience. The Rails philosophy includes two major guiding principles: -* **Don't Repeat Yourself:** DRY is a principle of software development which +* **Don't Repeat Yourself:** DRY is a principle of software development which states that "Every piece of knowledge must have a single, unambiguous, authoritative - representation within a system." By not writing the same information over and over + representation within a system." By not writing the same information over and over again, our code is more maintainable, more extensible, and less buggy. * **Convention Over Configuration:** Rails has opinions about the best way to do many things in a web application, and defaults to this set of conventions, rather than @@ -206,7 +206,7 @@ This will fire up WEBrick, a web server distributed with Ruby by default. To see your application in action, open a browser window and navigate to . You should see the Rails default information page: -![Welcome aboard screenshot](images/getting_started/rails_welcome.jpg) +![Welcome aboard screenshot](images/getting_started/rails_welcome.png) TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. To verify the server has stopped you should see your command prompt @@ -1121,8 +1121,8 @@ via the `PATCH` HTTP method which is the HTTP method you're expected to use to The first parameter of the `form_tag` can be an object, say, `@article` which would cause the helper to fill in the form with the fields of the object. Passing in a -symbol (`:article`) with the same name as the instance variable (`@article`) also -automagically leads to the same behavior. This is what is happening here. More details +symbol (`:article`) with the same name as the instance variable (`@article`) also +automagically leads to the same behavior. This is what is happening here. More details can be found in [form_for documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for). Next we need to create the `update` action in @@ -1396,7 +1396,7 @@ class CreateComments < ActiveRecord::Migration t.text :body # this line adds an integer column called `article_id`. - t.references :article, index: true + t.references :article, index: true t.timestamps end diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb index 0513066f5a979..1ac4e7f40ced8 100644 --- a/guides/source/layout.html.erb +++ b/guides/source/layout.html.erb @@ -4,7 +4,7 @@ - + <%= yield(:page_title) || 'Ruby on Rails Guides' %> diff --git a/guides/source/migrations.md b/guides/source/migrations.md index 64c4e1e07ef69..05443d5a5f98e 100644 --- a/guides/source/migrations.md +++ b/guides/source/migrations.md @@ -818,159 +818,6 @@ The `revert` method can be helpful when writing a new migration to undo previous migrations in whole or in part (see [Reverting Previous Migrations](#reverting-previous-migrations) above). -Using Models in Your Migrations -------------------------------- - -When creating or updating data in a migration it is often tempting to use one -of your models. After all, they exist to provide easy access to the underlying -data. This can be done, but some caution should be observed. - -For example, problems occur when the model uses database columns which are (1) -not currently in the database and (2) will be created by this or a subsequent -migration. - -Consider this example, where Alice and Bob are working on the same code base -which contains a `Product` model: - -Bob goes on vacation. - -Alice creates a migration for the `products` table which adds a new column and -initializes it: - -```ruby -# db/migrate/20100513121110_add_flag_to_product.rb - -class AddFlagToProduct < ActiveRecord::Migration - def change - add_column :products, :flag, :boolean - reversible do |dir| - dir.up { Product.update_all flag: false } - end - end -end -``` - -She also adds a validation to the `Product` model for the new column: - -```ruby -# app/models/product.rb - -class Product < ActiveRecord::Base - validates :flag, inclusion: { in: [true, false] } -end -``` - -Alice adds a second migration which adds another column to the `products` -table and initializes it: - -```ruby -# db/migrate/20100515121110_add_fuzz_to_product.rb - -class AddFuzzToProduct < ActiveRecord::Migration - def change - add_column :products, :fuzz, :string - reversible do |dir| - dir.up { Product.update_all fuzz: 'fuzzy' } - end - end -end -``` - -She also adds a validation to the `Product` model for the new column: - -```ruby -# app/models/product.rb - -class Product < ActiveRecord::Base - validates :flag, inclusion: { in: [true, false] } - validates :fuzz, presence: true -end -``` - -Both migrations work for Alice. - -Bob comes back from vacation and: - -* Updates the source - which contains both migrations and the latest version - of the Product model. -* Runs outstanding migrations with `rake db:migrate`, which - includes the one that updates the `Product` model. - -The migration crashes because when the model attempts to save, it tries to -validate the second added column, which is not in the database when the _first_ -migration runs: - -``` -rake aborted! -An error has occurred, this and all later migrations canceled: - -undefined method `fuzz' for # -``` - -A fix for this is to create a local model within the migration. This keeps -Rails from running the validations, so that the migrations run to completion. - -When using a local model, it's a good idea to call -`Product.reset_column_information` to refresh the Active Record cache for the -`Product` model prior to updating data in the database. - -If Alice had done this instead, there would have been no problem: - -```ruby -# db/migrate/20100513121110_add_flag_to_product.rb - -class AddFlagToProduct < ActiveRecord::Migration - class Product < ActiveRecord::Base - end - - def change - add_column :products, :flag, :boolean - Product.reset_column_information - reversible do |dir| - dir.up { Product.update_all flag: false } - end - end -end -``` - -```ruby -# db/migrate/20100515121110_add_fuzz_to_product.rb - -class AddFuzzToProduct < ActiveRecord::Migration - class Product < ActiveRecord::Base - end - - def change - add_column :products, :fuzz, :string - Product.reset_column_information - reversible do |dir| - dir.up { Product.update_all fuzz: 'fuzzy' } - end - end -end -``` - -There are other ways in which the above example could have gone badly. - -For example, imagine that Alice creates a migration that selectively -updates the `description` field on certain products. She runs the -migration, commits the code, and then begins working on the next feature, -which is to add a new column `fuzz` to the products table. - -She creates two migrations for this new feature, one which adds the new -column, and a second which selectively updates the `fuzz` column based on -other product attributes. - -These migrations run just fine, but when Bob comes back from his vacation -and calls `rake db:migrate` to run all the outstanding migrations, he gets a -subtle bug: The descriptions have defaults, and the `fuzz` column is present, -but `fuzz` is `nil` on all products. - -The solution is again to use `Product.reset_column_information` before -referencing the Product model in a migration, ensuring the Active Record's -knowledge of the table structure is current before manipulating data in those -records. - Schema Dumping and You ---------------------- diff --git a/guides/source/plugins.md b/guides/source/plugins.md index 720ca5d1175e4..fe4215839f2ca 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -92,12 +92,12 @@ Run `rake` to run the test. This test should fail because we haven't implemented Great - now you are ready to start development. -In `lib/yaffle.rb`, add `require "yaffle/core_ext"`: +In `lib/yaffle.rb`, add `require 'yaffle/core_ext'`: ```ruby # yaffle/lib/yaffle.rb -require "yaffle/core_ext" +require 'yaffle/core_ext' module Yaffle end @@ -149,7 +149,7 @@ end ```ruby # yaffle/lib/yaffle.rb -require "yaffle/core_ext" +require 'yaffle/core_ext' require 'yaffle/acts_as_yaffle' module Yaffle diff --git a/guides/source/testing.md b/guides/source/testing.md index 07f3aad1e6b75..aa37115d14929 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -248,7 +248,7 @@ To see how a test failure is reported, you can add a failing test to the `post_t ```ruby test "should not save post without title" do post = Post.new - assert !post.save + assert_not post.save end ``` @@ -272,7 +272,7 @@ In the output, `F` denotes a failure. You can see the corresponding trace shown ```ruby test "should not save post without title" do post = Post.new - assert !post.save, "Saved the post without a title" + assert_not post.save, "Saved the post without a title" end ``` @@ -943,7 +943,7 @@ class UserMailerTest < ActionMailer::TestCase # Send the email, then test that it got queued email = UserMailer.create_invite('me@example.com', 'friend@example.com', Time.now).deliver - assert !ActionMailer::Base.deliveries.empty? + assert_not ActionMailer::Base.deliveries.empty? # Test the body of the sent email contains what we expect it to assert_equal ['me@example.com'], email.from diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index af3580a85b966..da124e21a4cef 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -669,7 +669,7 @@ 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.16, +The following changes are meant for upgrading your application to Rails 3.2.17, the last 3.2.x version of Rails. ### Gemfile @@ -677,7 +677,7 @@ the last 3.2.x version of Rails. Make the following changes to your `Gemfile`. ```ruby -gem 'rails', '3.2.16' +gem 'rails', '3.2.17' group :assets do gem 'sass-rails', '~> 3.2.6' diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index bade9ef54369c..18f2546c730d8 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,319 +1 @@ -* Do not crash when `config/secrets.yml` is empty. - - *Yves Senn* - -* Set `dump_schema_after_migration` config values in production. - - Set `config.active_record.dump_schema_after_migration` as false - in the generated `config/environments/production.rb` file. - - *Emil Soman* - -* Added Thor-action for creation of migrations. - - Fixes #13588, #12674. - - *Gert Goet* - -* Ensure that `bin/rails` is a file before trying to execute it. - - Fixes #13825. - - *bronzle* - -* Use single quotes in generated files. - - *Cristian Mircea Messel*, *Chulki Lee* - -* Only lookup `config.log_level` for stdlib `::Logger` instances. - Assign it as is for third party loggers like `Log4r::Logger`. - - Fixes #13421. - - *Yves Senn* - -* The `Gemfile` of new applications depends on SDoc ~> 0.4.0. - - *Xavier Noria* - -* `test_help.rb` now automatically checks/maintains your test database - schema. (Use `config.active_record.maintain_test_schema = false` to - disable.) - - *Jon Leighton* - -* Configure `secrets.yml` and `database.yml` to read configuration - from the system environment by default for production. - - *José Valim* - -* `config.assets.raise_runtime_errors` is set to true by default - - This option has been introduced in - [sprockets-rails#100][https://github.com/rails/sprockets-rails/pull/100] - and defaults to true in new applications in development. - - *Richard Schneeman* - -* Generates `html` and `text` templates for mailers by default. - - *Kassio Borges* - -* Move `secret_key_base` from `config/initializers/secret_token.rb` - to `config/secrets.yml`. - - `secret_key_base` is now saved in `Rails.application.secrets.secret_key_base` - and it fallbacks to the value of `config.secret_key_base` when it is not - present in `config/secrets.yml`. - - `config/initializers/secret_token.rb` is not generated by default - in new applications. - - *Guillermo Iguaran* - -* Generate a new `secrets.yml` file in the `config` folder for new - applications. By default, this file contains the application's `secret_key_base`, - but it could also be used to store other secrets such as access keys for external - APIs. - - The secrets added to this file will be accessible via `Rails.application.secrets`. - For example, with the following `secrets.yml`: - - development: - secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 - some_api_key: SOMEKEY - - `Rails.application.secrets.some_api_key` will return `SOMEKEY` in the development - environment. - - *Guillermo Iguaran* - -* Add `ENV['DATABASE_URL']` support in `rails dbconsole`. Fixes #13320. - - *Huiming Teo* - -* Add `Application#message_verifier` method to return a message verifier. - - This verifier can be used to generate and verify signed messages in the application. - - message = Rails.application.message_verifier(:sensitive_data).generate('my sensible data') - Rails.application.message_verifier(:sensitive_data).verify(message) - # => 'my sensible data' - - It is recommended not to use the same verifier for different things, so you can get different - verifiers passing the name argument. - - message = Rails.application.message_verifier(:cookies).generate('my sensible cookie data') - - See the `ActiveSupport::MessageVerifier` documentation for more information. - - *Rafael Mendonça França* - -* The [Spring application - preloader](https://github.com/rails/spring) is now installed - by default for new applications. It uses the development group of - the Gemfile, so will not be installed in production. - - *Jon Leighton* - -* Uses .railsrc while creating new plugin if it is available. - Fixes #10700. - - *Prathamesh Sonpatki* - -* Remove turbolinks when generating a new application based on a template that skips it. - - Example: - - Skips turbolinks adding `add_gem_entry_filter { |gem| gem.name != "turbolinks" }` - to the template. - - *Lauro Caetano* - -* Instrument an `load_config_initializer.railties` event on each load of configuration initializer - from `config/initializers`. Subscribers should be attached before `load_config_initializers` - initializer completed. - - Registering subscriber examples: - - # config/application.rb - module RailsApp - class Application < Rails::Application - ActiveSupport::Notifications.subscribe('load_config_initializer.railties') do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - puts "Loaded initializer #{event.payload[:initializer]} (#{event.duration}ms)" - end - end - end - - # my_engine/lib/my_engine/engine.rb - module MyEngine - class Engine < ::Rails::Engine - config.before_initialize do - ActiveSupport::Notifications.subscribe('load_config_initializer.railties') do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - puts "Loaded initializer #{event.payload[:initializer]} (#{event.duration}ms)" - end - end - end - end - - *Paul Nikitochkin* - -* Support for Pathnames in eager load paths. - - *Mike Pack* - -* Fixed missing line and shadow on service pages(404, 422, 500). - - *Dmitry Korotkov* - -* `BACKTRACE` environment variable to show unfiltered backtraces for - test failures. - - Example: - - $ BACKTRACE=1 ruby -Itest ... - # or with rake - $ BACKTRACE=1 bin/rake - - *Yves Senn* - -* Removal of all javascript stuff (gems and files) when generating a new - application using the `--skip-javascript` option. - - *Robin Dupret* - -* Make the application name snake cased when it contains spaces - - The application name is used to fill the `database.yml` and - `session_store.rb` files ; previously, if the provided name - contained whitespaces, it led to unexpected names in these files. - - *Robin Dupret* - -* Added `--model-name` option to `ScaffoldControllerGenerator`. - - *yalab* - -* Expose MiddlewareStack#unshift to environment configuration. - - *Ben Pickles* - -* `rails server` will only extend the logger to output to STDOUT - in development environment. - - *Richard Schneeman* - -* Don't require passing path to app before options in `rails new` - and `rails plugin new` - - *Piotr Sarnacki* - -* rake notes now searches *.less files - - *Josh Crowder* - -* Generate nested route for namespaced controller generated using - `rails g controller`. - Fixes #11532. - - Example: - - rails g controller admin/dashboard index - - # Before: - get "dashboard/index" - - # After: - namespace :admin do - get "dashboard/index" - end - - *Prathamesh Sonpatki* - -* Fix the event name of action_dispatch requests. - - *Rafael Mendonça França* - -* Make `config.log_level` work with custom loggers. - - *Max Shytikov* - -* Changed stylesheet load order in the stylesheet manifest generator. - Fixes #11639. - - *Pawel Janiak* - -* Added generated unit test for generator generator using new - `test:generators` rake task. - - *Josef Šimánek* - -* Removed `update:application_controller` rake task. - - *Josef Šimánek* - -* Fix `rake environment` to do not eager load modules - - *Paul Nikitochkin* - -* Fix `rake notes` to look into `*.sass` files - - *Yuri Artemev* - -* Removed deprecated `Rails.application.railties.engines`. - - *Arun Agrawal* - -* Removed deprecated threadsafe! from Rails Config. - - *Paul Nikitochkin* - -* Remove deprecated `ActiveRecord::Generators::ActiveModel#update_attributes` in - favor of `ActiveRecord::Generators::ActiveModel#update`. - - *Vipul A M* - -* Remove deprecated `config.whiny_nils` option. - - *Vipul A M* - -* Rename `commands/plugin_new.rb` to `commands/plugin.rb` and fix references - - *Richard Schneeman* - -* Fix `rails plugin --help` command. - - *Richard Schneeman* - -* Omit turbolinks configuration completely on skip_javascript generator option. - - *Nikita Fedyashev* - -* Removed deprecated rake tasks for running tests: `rake test:uncommitted` and - `rake test:recent`. - - *John Wang* - -* Clearing autoloaded constants triggers routes reloading. - Fixes #10685. - - *Xavier Noria* - -* Fixes bug with scaffold generator with `--assets=false --resource-route=false`. - Fixes #9525. - - *Arun Agrawal* - -* Rails::Railtie no longer forces the Rails::Configurable module on everything - that subclasses it. Instead, the methods from Rails::Configurable have been - moved to class methods in Railtie and the Railtie has been made abstract. - - *John Wang* - -* Changes repetitive th tags to use colspan attribute in `index.html.erb` template. - - *Sıtkı Bağdat* - -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/railties/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/railties/CHANGELOG.md) for previous changes. diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index 33bcab1e57456..a26d41c0cf41a 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -53,11 +53,7 @@ module Bootstrap logger end - if ::Logger === Rails.logger - Rails.logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase) - else - Rails.logger.level = config.log_level - end + Rails.logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase) end # Initialize cache early in the stack so railties can make use of it. diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index f1f79d83783b7..b2ecc22294480 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -111,7 +111,6 @@ def gemfile_entries javascript_gemfile_entry, jbuilder_gemfile_entry, sdoc_gemfile_entry, - platform_dependent_gemfile_entry, spring_gemfile_entry, @extra_entries].flatten.find_all(&@gem_filter) end @@ -258,14 +257,6 @@ def assets_gemfile_entry gems end - def platform_dependent_gemfile_entry - gems = [] - if RUBY_ENGINE == 'rbx' - gems << GemfileEntry.version('rubysl', nil) - end - gems - end - def jbuilder_gemfile_entry comment = 'Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder' GemfileEntry.version('jbuilder', '~> 2.0', comment) diff --git a/railties/lib/rails/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb new file mode 100644 index 0000000000000..c4f45d344b967 --- /dev/null +++ b/railties/lib/rails/generators/model_helpers.rb @@ -0,0 +1,28 @@ +require 'rails/generators/active_model' + +module Rails + module Generators + module ModelHelpers # :nodoc: + PLURAL_MODEL_NAME_WARN_MESSAGE = "The model name '%s' was recognized as a plural, using the singular '%s'. " \ + "Override with --force-plural or setup custom inflection rules for this noun before running the generator." + mattr_accessor :skip_warn + + def self.included(base) #:nodoc: + base.class_option :force_plural, type: :boolean, default: false, desc: 'Forces the use of the given model name' + end + + def initialize(args, *_options) + super + if name == name.pluralize && name.singularize != name.pluralize && !options[:force_plural] + singular = name.singularize + unless ModelHelpers.skip_warn + say PLURAL_MODEL_NAME_WARN_MESSAGE % [name, singular] + ModelHelpers.skip_warn = true + end + name.replace singular + assign_names!(name) + end + end + end + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index 6d017e187de6a..a9b6787894952 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -14,7 +14,7 @@ source 'https://rubygems.org' <% end -%> # Use ActiveModel has_secure_password -# gem 'bcrypt-ruby', '~> 3.1.2' +# gem 'bcrypt', '~> 3.1.7' # Use unicorn as the app server # gem 'unicorn' 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 d9cc60d656d99..b789ed9a94721 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 @@ -35,6 +35,10 @@ Rails.application.configure do # Version of your assets, change this if you want to expire all your assets. config.assets.version = '1.0' + + # 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 -%> # Specifies the header that your server uses for sending files. @@ -59,12 +63,6 @@ Rails.application.configure do # 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 diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb index ea3d69d7c96cd..87bab129bbcd4 100644 --- a/railties/lib/rails/generators/rails/model/model_generator.rb +++ b/railties/lib/rails/generators/rails/model/model_generator.rb @@ -1,6 +1,10 @@ +require 'rails/generators/model_helpers' + module Rails module Generators class ModelGenerator < NamedBase # :nodoc: + include Rails::Generators::ModelHelpers + argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]" hook_for :orm, required: true end diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb index 7329ee9f4855f..46699351569b0 100644 --- a/railties/lib/rails/generators/resource_helpers.rb +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -1,14 +1,14 @@ require 'rails/generators/active_model' +require 'rails/generators/model_helpers' module Rails module Generators # Deal with controller names on scaffold and add some helpers to deal with # ActiveModel. module ResourceHelpers # :nodoc: - mattr_accessor :skip_warn def self.included(base) #:nodoc: - base.class_option :force_plural, type: :boolean, desc: "Forces the use of a plural ModelName" + base.send :include, Rails::Generators::ModelHelpers base.class_option :model_name, type: :string, desc: "ModelName to be used" end @@ -21,15 +21,6 @@ def initialize(*args) #:nodoc: assign_names!(self.name) end - if name == name.pluralize && name.singularize != name.pluralize && !options[:force_plural] - unless ResourceHelpers.skip_warn - say "Plural version of the model detected, using singularized version. Override with --force-plural." - ResourceHelpers.skip_warn = true - end - name.replace name.singularize - assign_names!(name) - end - assign_controller_names!(controller_name.pluralize) end diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index e4fd798d18c4a..79313c936aca9 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -1,9 +1,9 @@ module Rails module VERSION MAJOR = 4 - MINOR = 1 + MINOR = 2 TINY = 0 - PRE = "beta2" + PRE = "alpha" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index b2d0e7e202934..b39cd3747b78b 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -762,7 +762,7 @@ def index end end - test "lookup config.log_level with custom logger (stdlib Logger)" do + test "config.log_level with custom logger" do make_basic_app do |app| app.config.logger = Logger.new(STDOUT) app.config.log_level = :info @@ -770,19 +770,6 @@ def index assert_equal Logger::INFO, Rails.logger.level end - test "assign log_level as is with custom logger (third party logger)" do - logger_class = Class.new do - attr_accessor :level - end - logger_instance = logger_class.new - make_basic_app do |app| - app.config.logger = logger_instance - app.config.log_level = :info - end - assert_equal logger_instance, Rails.logger - assert_equal :info, Rails.logger.level - end - test "respond_to? accepts include_private" do make_basic_app diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 5811379e35250..5ebdadacbf4c8 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -265,13 +265,6 @@ def test_inclusion_of_javascript_runtime end end - def test_inclusion_of_plateform_dependent_gems - run_generator([destination_root]) - if RUBY_ENGINE == 'rbx' - assert_gem 'rubysl' - end - end - def test_jquery_is_the_default_javascript_library run_generator assert_file "app/assets/javascripts/application.js" do |contents| diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 01ab77ee20c29..bdf51b457ca97 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -34,6 +34,13 @@ def test_model_with_parent_option assert_no_migration "db/migrate/create_accounts.rb" end + def test_plural_names_are_singularized + content = run_generator ["accounts".freeze] + assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/ + assert_file "test/models/account_test.rb", /class AccountTest/ + assert_match(/The model name 'accounts' was recognized as a plural, using the singular 'account'\. Override with --force-plural or setup custom inflection rules for this noun before running the generator\./, content) + end + def test_model_with_underscored_parent_option run_generator ["account", "--parent", "admin/account"] assert_file "app/models/account.rb", /class Account < Admin::Account/ diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 3d4e694361536..55c8d92ee8277 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -63,7 +63,7 @@ def test_plural_names_are_singularized content = run_generator ["accounts".freeze] assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/ assert_file "test/models/account_test.rb", /class AccountTest/ - assert_match(/Plural version of the model detected, using singularized version. Override with --force-plural./, content) + assert_match(/The model name 'accounts' was recognized as a plural, using the singular 'account'\. Override with --force-plural or setup custom inflection rules for this noun before running the generator\./, content) end def test_plural_names_can_be_forced diff --git a/version.rb b/version.rb index e4fd798d18c4a..79313c936aca9 100644 --- a/version.rb +++ b/version.rb @@ -1,9 +1,9 @@ module Rails module VERSION MAJOR = 4 - MINOR = 1 + MINOR = 2 TINY = 0 - PRE = "beta2" + PRE = "alpha" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end