diff options
68 files changed, 566 insertions, 4075 deletions
diff --git a/RAILS_VERSION b/RAILS_VERSION index ee00187eb3..59e61f95df 100644 --- a/RAILS_VERSION +++ b/RAILS_VERSION @@ -1 +1 @@ -4.1.0.beta2 +4.2.0.alpha diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 5a61746700..6203699405 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/version.rb b/actionmailer/lib/action_mailer/version.rb index 60732c593b..7b2cf305c2 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 b05aa21f95..68b5213bfc 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 `<script>` tags. - - *Jeremy Kemper* - -* Fix generating a path for engine inside a resources block. - - Fixes #8533. - - *Piotr Sarnacki* - -* Add `Mime::Type.register "text/vcard", :vcf` to the default list of mime types. - - *DHH* - -* Remove deprecated `ActionController::RecordIdentifier`, use - `ActionView::RecordIdentifier` instead. - - *kennyj* - -* Fix regression when using `ActionView::Helpers::TranslationHelper#translate` with - `options[:raise]`. - - This regression was introduced at ec16ba75a5493b9da972eea08bae630eba35b62f. - - *Shota Fukumori (sora_h)* - -* Introducing Variants - - We often want to render different html/json/xml templates for phones, - tablets, and desktop browsers. Variants make it easy. - - The request variant is a specialization of the request format, like `:tablet`, - `:phone`, or `:desktop`. - - You can set the variant in a `before_action`: - - request.variant = :tablet if request.user_agent =~ /iPad/ - - Respond to variants in the action just like you respond to formats: - - respond_to do |format| - format.html do |html| - html.tablet # renders app/views/projects/show.html+tablet.erb - html.phone { extra_setup; render ... } - end - end - - Provide separate templates for each format and variant: - - app/views/projects/show.html.erb - app/views/projects/show.html+tablet.erb - app/views/projects/show.html+phone.erb - - You can also simplify the variants definition using the inline syntax: - - respond_to do |format| - format.js { render "trash" } - format.html.phone { redirect_to progress_path } - format.html.none { render "trash" } - end - - Variants also support common `any`/`all` block that formats have. - - It works for both inline: - - respond_to do |format| - format.html.any { render text: "any" } - format.html.phone { render text: "phone" } - end - - and block syntax: - - respond_to do |format| - format.html do |variant| - variant.any(:tablet, :phablet){ render text: "any" } - variant.phone { render text: "phone" } - end - end - - *Łukasz Strzałkowski* - -* Fix render of localized templates without an explicit format using wrong - content header and not passing correct formats to template due to the - introduction of the `NullType` for mimes. - - Templates like `hello.it.erb` were subject to this issue. - - Fixes #13064. - - *Angelo Capilleri*, *Carlos Antonio da Silva* - -* Try to escape each part of a url correctly when using a redirect route. - - Fixes #13110. - - *Andrew White* - -* Better error message for typos in assert_response argument. - - When the response type argument to `assert_response` is not a known - response type, `assert_response` now throws an ArgumentError with a clear - message. This is intended to help debug typos in the response type. - - *Victor Costan* - -* Fix formatting for `rake routes` when a section is shorter than a header. - - *Sıtkı Bağdat* - -* Take a hash with options inside array in `#url_for`. - - Example: - - url_for [:new, :admin, :post, { param: 'value' }] - # => http://example.com/admin/posts/new?param=value - - *Andrey Ognevsky* - -* Add `session#fetch` method - - fetch behaves like [Hash#fetch](http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch). - It returns a value from the hash for the given key. - If the key can’t be found, there are several options: - - * With no other arguments, it will raise an KeyError exception. - * If a default value is given, then that will be returned. - * If the optional code block is specified, then that will be run and its result returned. - - *Damien Mathieu* - -* Don't let strong parameters mutate the given hash via `fetch` - - Create a new instance if the given parameter is a `Hash` instead of - passing it to the `convert_hashes_to_parameters` method since it is - overriding its default value. - - *Brendon Murphy*, *Doug Cole* - -* Add `params` option to `button_to` form helper, which renders the given hash - as hidden form fields. - - *Andy Waite* - -* Make assets helpers work in the controllers like it works in the views. - - Example: - - # config/application.rb - config.asset_host = 'http://mycdn.com' - - ActionController::Base.helpers.asset_path('fallback.png') - # => http://mycdn.com/assets/fallback.png - - Fixes #10051. - - *Tima Maslyuchenko* - -* Respect `SCRIPT_NAME` when using `redirect` with a relative path - - Example: - - # application routes.rb - mount BlogEngine => '/blog' - - # engine routes.rb - get '/admin' => redirect('admin/dashboard') - - This now redirects to the path `/blog/admin/dashboard`, whereas before it would've - generated an invalid url because there would be no slash between the host name and - the path. It also allows redirects to work where the application is deployed to a - subdirectory of a website. - - Fixes #7977. - - *Andrew White* - -* Fixing repond_with working directly on the options hash - This fixes an issue where the respond_with worked directly with the given - options hash, so that if a user relied on it after calling respond_with, - the hash wouldn't be the same. - - Fixes #12029. - - *bluehotdog* - -* Fix `ActionDispatch::RemoteIp::GetIp#calculate_ip` to only check for spoofing - attacks if both `HTTP_CLIENT_IP` and `HTTP_X_FORWARDED_FOR` are set. - - Fixes #10844. - - *Tamir Duberstein* - -* Strong parameters should permit nested number as key. - - Fixes #12293. - - *kennyj* - -* Fix regex used to detect URI schemes in `redirect_to` to be consistent with - RFC 3986. - - *Derek Prior* - -* Fix incorrect `assert_redirected_to` failure message for protocol-relative - URLs. - - *Derek Prior* - -* Fix an issue where router can't recognize downcased url encoding path. - - Fixes #12269. - - *kennyj* - -* Fix custom flash type definition. Misusage of the `_flash_types` class variable - caused an error when reloading controllers with custom flash types. - - Fixes #12057. - - *Ricardo de Cillo* - -* Do not break params filtering on `nil` values. - - Fixes #12149. - - *Vasiliy Ermolovich* - -* Development mode exceptions are rendered in text format in case of XHR request. - - *Kir Shatrov* - -* Fix an issue where :if and :unless controller action procs were being run - before checking for the correct action in the :only and :unless options. - - Fixes #11799. - - *Nicholas Jakobsen* - -* Fix an issue where `assert_dom_equal` and `assert_dom_not_equal` were - ignoring the passed failure message argument. - - Fixes #11751. - - *Ryan McGeary* - -* Allow REMOTE_ADDR, HTTP_HOST and HTTP_USER_AGENT to be overridden from - the environment passed into `ActionDispatch::TestRequest.new`. - - Fixes #11590. - - *Andrew White* - -* Fix an issue where Journey was failing to clear the named routes hash when the - routes were reloaded and since it doesn't overwrite existing routes then if a - route changed but wasn't renamed it kept the old definition. This was being - masked by the optimised url helpers so it only became apparent when passing an - options hash to the url helper. - - *Andrew White* - -* Skip routes pointing to a redirect or mounted application when generating urls - using an options hash as they aren't relevant and generate incorrect urls. - - Fixes #8018. - - *Andrew White* - -* Move `MissingHelperError` out of the `ClassMethods` module. - - *Yves Senn* - -* Fix an issue where rails raise exception about missing helper where it - should throw `LoadError`. When helper file exists and only loaded file from - this helper does not exist rails should throw LoadError instead of - `MissingHelperError`. - - *Piotr Niełacny* - -* Fix `ActionDispatch::ParamsParser#parse_formatted_parameters` to rewind body input stream on - parsing json params. - - Fixes #11345. - - *Yuri Bol*, *Paul Nikitochkin* - -* Ignore spaces around delimiter in Set-Cookie header. - - *Yamagishi Kazutoshi* - -* Remove deprecated Rails application fallback for integration testing, set - `ActionDispatch.test_app` instead. - - *Carlos Antonio da Silva* - -* Remove deprecated `page_cache_extension` config. - - *Francesco Rodriguez* - -* Remove deprecated constants from Action Controller: - - ActionController::AbstractRequest => ActionDispatch::Request - ActionController::Request => ActionDispatch::Request - ActionController::AbstractResponse => ActionDispatch::Response - ActionController::Response => ActionDispatch::Response - ActionController::Routing => ActionDispatch::Routing - ActionController::Integration => ActionDispatch::Integration - ActionController::IntegrationTest => ActionDispatch::IntegrationTest - - *Carlos Antonio da Silva* - -* Fix `Mime::Type.parse` when bad accepts header is looked up. Previously it - was setting `request.formats` with an array containing a `nil` value, which - raised an error when setting the controller formats. - - Fixes #10965. - - *Becker* - -* Merge `:action` from routing scope and assign endpoint if both `:controller` - and `:action` are present. The endpoint assignment only occurs if there is - no `:to` present in the options hash so should only affect routes using the - shorthand syntax (i.e. endpoint is inferred from the path). - - Fixes #9856. - - *Yves Senn*, *Andrew White* - -* Action View extracted from Action Pack. - - *Piotr Sarnacki*, *Łukasz Strzałkowski* - -Please check [4-0-stable](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/actionpack/CHANGELOG.md) for previous changes. diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index 823a1050b5..e920a33765 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -50,7 +50,7 @@ module ActionController def unpermitted_parameters(event) unpermitted_keys = event.payload[:keys] - debug("Unpermitted parameters: #{unpermitted_keys.join(", ")}") + debug("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}") end def deep_munge(event) diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 48a916f2b1..aff083b502 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -32,7 +32,7 @@ module ActionController def initialize(params) # :nodoc: @params = params - super("found unpermitted parameters: #{params.join(", ")}") + super("found unpermitted parameter#{'s' if params.size > 1 }: #{params.join(", ")}") end end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 3dd2e2a45c..11b5e6be33 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -52,7 +52,6 @@ module ActionDispatch autoload :DebugExceptions autoload :ExceptionWrapper autoload :Flash - autoload :Head autoload :ParamsParser autoload :PublicExceptions autoload :Reloader diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 8da3069c8b..75fb0d9532 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,7 +1,7 @@ module ActionPack # Returns the version of the currently loaded ActionPack 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/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb index 22e603b881..9ce04b9aeb 100644 --- a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb +++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb @@ -10,23 +10,45 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase ActionController::Parameters.action_on_unpermitted_parameters = false end - test "logs on unexpected params" do + test "logs on unexpected param" do params = ActionController::Parameters.new({ book: { pages: 65 }, fishing: "Turnips" }) - assert_logged("Unpermitted parameters: fishing") do + assert_logged("Unpermitted parameter: fishing") do params.permit(book: [:pages]) end end - test "logs on unexpected nested params" do + test "logs on unexpected params" do + params = ActionController::Parameters.new({ + book: { pages: 65 }, + fishing: "Turnips", + car: "Mersedes" + }) + + assert_logged("Unpermitted parameters: fishing, car") do + params.permit(book: [:pages]) + end + end + + test "logs on unexpected nested param" do params = ActionController::Parameters.new({ book: { pages: 65, title: "Green Cats and where to find then." } }) - assert_logged("Unpermitted parameters: title") do + assert_logged("Unpermitted parameter: title") do + params.permit(book: [:pages]) + end + end + + test "logs on unexpected nested params" do + params = ActionController::Parameters.new({ + book: { pages: 65, title: "Green Cats and where to find then.", author: "G. A. Dog" } + }) + + assert_logged("Unpermitted parameters: title, author") do params.permit(book: [:pages]) end end diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index c05ed10263..28508a5dfa 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,327 +1 @@ -* Added `:plain`, `:html` and `:body` option for `render` method. Please see - Action Pack's release note for more detail. - - *Prem Sichanugrist* - -* Date select helpers accept a format string for the months selector via the - new option `:month_format_string`. - - When rendered, the format string gets passed keys `:number` (integer), and - `:name` (string), in order to be able to interpolate them as in - - '%{name} (%<number>02d)' - - for example. - - This option is motivated by #13618. - - *Xavier Noria* - -* Added `config.action_view.raise_on_missing_translations` to define whether an - error should be raised for missing translations. - - Fixes #13196. - - *Kassio Borges* - -* Improved ERB dependency detection. New argument types and formattings for the `render` - calls can be matched. - - Fixes #13074, #13116. - - *João Britto* - -* Use `display:none` instead of `display:inline` for hidden fields. - - Fixes #6403. - - *Gaelian Ditchburn* - -* The `video_tag` helper accepts a number as `:size`. - - The `:size` option of the `video_tag` helper now can be specified - with a stringified number. The `width` and `height` attributes of - the generated tag will be the same. - - *Kuldeep Aggarwal* - -* Escape format, negative_format and units options of number helpers - - Fixes: CVE-2014-0081 - -* A Cycle object should accept an array and cycle through it as it would with a set of - comma-separated objects. - - arr = [1,2,3] - cycle(arr) # => '1' - cycle(arr) # => '2' - cycle(arr) # => '3' - - Previously, it would return the array as a string, because it took the array as a - single object: - - arr = [1,2,3] - cycle(arr) # => '[1,2,3]' - cycle(arr) # => '[1,2,3]' - cycle(arr) # => '[1,2,3]' - - *Kristian Freeman* - -* Label tags generated by collection helpers only inherit the `:index` and - `:namespace` from the input, because only these attributes modifies the - `for` attribute of the label. Also, the input attributes don't have - precedence over the label attributes anymore. - - Before: - - collection = [[1, true, { class: 'foo' }]] - f.collection_check_boxes :options, collection, :second, :first do |b| - b.label(class: 'my_custom_class') - end - - # => <label class="foo" for="user_active_true">1</label> - - After: - - collection = [[1, true, { class: 'foo' }]] - f.collection_check_boxes :options, collection, :second, :first do |b| - b.label(class: 'my_custom_class') - end - - # => <label class="my_custom_class" for="user_active_true">1</label> - - *Andriel Nuernberg* - -* Fixed a long-standing bug in `json_escape` that causes quotation marks to be stripped. - This method also escapes the \u2028 and \u2029 unicode newline characters which are - treated as \n in JavaScript. This matches the behaviour of the AS::JSON encoder. (The - original change in the encoder was introduced in #10534.) - - *Godfrey Chan* - -* `ActionView::MissingTemplate` includes underscore when raised for a partial. - - Fixes #13002. - - *Yves Senn* - -* Use `set_backtrace` instead of instance variable `@backtrace` in ActionView exceptions. - - *Shimpei Makimoto* - -* Fix `simple_format` escapes own output when passing `sanitize: true`. - - *Paul Seidemann* - -* Ensure `ActionView::Digestor.cache` is correctly cleaned up when - combining recursive templates with `ActionView::Resolver.caching = false`. - - *wyaeld* - -* Fix `collection_check_boxes` generated hidden input to use the name attribute provided - in the options hash. - - *Angel N. Sciortino* - -* Fix some edge cases for AV `select` helper with `:selected` option. - - *Bogdan Gusiev* - -* Ability to pass a block to the `select` helper. - - Example: - - <%= select(report, "campaign_ids") do %> - <% available_campaigns.each do |c| -%> - <%= content_tag(:option, c.name, value: c.id, data: { tags: c.tags.to_json }) %> - <% end -%> - <% end -%> - - *Bogdan Gusiev* - -* Handle `:namespace` form option in collection labels. - - *Vasiliy Ermolovich* - -* Fix `form_for` when both `namespace` and `as` options are present. - - `as` option no longer overwrites `namespace` option when generating - html id attribute of the form element. - - *Adam Niedzielski* - -* Fix `excerpt` when `:separator` is `nil`. - - *Paul Nikitochkin* - -* Only cache template digests if `config.cache_template_loading` is true. - - *Josh Lauer*, *Justin Ridgewell* - -* Fixed a bug where the lookup details were not being taken into account - when caching the digest of a template - changes to the details now - cause a different cache key to be used. - - *Daniel Schierbeck* - -* Added an `extname` hash option for `javascript_include_tag` method. - - Before: - - javascript_include_tag('templates.jst') - # => <script src="/javascripts/templates.jst.js"></script> - - After: - - javascript_include_tag('templates.jst', extname: false ) - # => <script src="/javascripts/templates.jst"></script> - - *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 #=> <input value=""> - f.text_area :field, value: nil #=> <textarea>value of field</textarea> - - After: - - f.text_area :field, value: nil #=> <textarea></textarea> - - *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') } - # => "<a action=\"bar\" controller=\"foo\"><span>Example site</span></a>" - - After: - - link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') } - # => "<a href=\"/foo/bar\"><span>Example site</span></a>" - - *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 - # => '<form>..<input name="utf8" type="hidden" value="✓" />..</form>' - - After: - - form_tag - # => '<form>..<input name="utf8" type="hidden" value="✓" />..</form>' - - form_tag({}, { :enforce_utf8 => false }) - # => '<form>....</form>' - - *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, '<b>Showing</b>', 'github.com') - # => "<b>Showing</b>" - - After: - - link_to_unless(true, '<b>Showing</b>', '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") - # => "<img alt=\"Google\" src=\"/assets/HTTP://google.com\" />" - image_tag("http://google.com") - # => "<img alt=\"Google\" src=\"http://google.com\" />" - - After: - - image_tag("HTTP://google.com") - # => "<img alt=\"Google\" src=\"HTTP://google.com\" />" - image_tag("http://google.com") - # => "<img alt=\"Google\" src=\"http://google.com\" />" - - *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 35d3ba8434..6335e3dd4d 100644 --- a/actionview/lib/action_view/helpers/tags/label.rb +++ b/actionview/lib/action_view/helpers/tags/label.rb @@ -36,7 +36,7 @@ module ActionView 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 3ccace1274..51379d433f 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -82,7 +82,7 @@ module ActionView # to using GET. If <tt>href: '#'</tt> 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 <tt>post?</tt>, <tt>delete?</tt>, <tt>:patch</tt>, or <tt>put?</tt>. + # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>patch?</tt>, or <tt>put?</tt>. # * <tt>remote: true</tt> - 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 3d5d6c9be1..b3ff415775 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 fe82349265..b5e9801776 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -143,75 +143,68 @@ class FormHelperTest < ActionView::TestCase end def test_label_with_locales_strings - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_body">Write entire text here</label>', label("post", "body")) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_body">Write entire text here</label>', label("post", "body")) + end end def test_label_with_human_attribute_name - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_cost">Total cost</label>', label(:post, :cost)) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_cost">Total cost</label>', label(:post, :cost)) + end end def test_label_with_locales_symbols - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_body">Write entire text here</label>', label(:post, :body)) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_body">Write entire text here</label>', label(:post, :body)) + end end def test_label_with_locales_and_options - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal( - '<label for="post_body" class="post_body">Write entire text here</label>', - label(:post, :body, class: "post_body") - ) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal( + '<label for="post_body" class="post_body">Write entire text here</label>', + 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 for="post_color_red">Rojo</label>', label(:post, :color, value: "red")) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_color_red">Rojo</label>', 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 - '<label for="post_comments_attributes_0_body">Write body here</label>' - end + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<label for="post_comments_attributes_0_body">Write body here</label>' + 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 - '<label for="post_tags_attributes_0_value">Tag</label>' - end + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<label for="post_tags_attributes_0_value">Tag</label>' + 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 @@ class FormHelperTest < ActionView::TestCase 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 + "<input name='commit' type='submit' value='Create Post' />" + end - expected = whole_form('/posts', 'new_post', 'new_post') do - "<input name='commit' type='submit' value='Create Post' />" + 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 + "<input name='commit' type='submit' value='Confirm Post changes' />" + end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do - "<input name='commit' type='submit' value='Confirm Post changes' />" + 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 + "<input name='commit' class='extra' type='submit' value='Save changes' />" + end - expected = whole_form do - "<input name='commit' class='extra' type='submit' value='Save changes' />" + 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 + "<input name='commit' type='submit' value='Update your Post' />" + end - expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do - "<input name='commit' type='submit' value='Update your Post' />" + 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 @@ class FormHelperTest < ActionView::TestCase 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 @@ class FormHelperTest < ActionView::TestCase 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 500d8dc42f..f9246e6c81 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/version.rb b/activemodel/lib/active_model/version.rb index f7c9534ffb..5c9d2bc6bc 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 7efd75a239..58ad2b84fd 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,1858 +1,27 @@ -* Default scopes are no longer overriden by chained conditions. - - 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. - - Before: - - 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" = '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:<tasks>` - - `ActiveRecord.establish_connection` - - - With Rails - - `rake db:<tasks>` - - `rails <server> | <console>` - - `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. +* Coerce strings when reading attributes. + Fixes #10485. Example: - Author.inspect # => "Author(no database connection)" + book = Book.new(title: 12345) + book.save! + book.title # => "12345" *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'`. +* 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. - 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. + 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* -* 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* - -* 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. - - 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 +* Support for user created range types in PostgreSQL. *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. - - 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 - - *Yves Senn* - -* Fix the `:primary_key` option for `has_many` associations. - - Fixes #10693. - - *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/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 759e162e19..fd185de589 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 @@ module ActiveRecord # 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 ad069f5e53..7f530ec5e4 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 @@ module ActiveRecord # 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 f2fbd5a8f2..187eefb9e4 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -87,7 +87,7 @@ module ActiveRecord 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 @@ module ActiveRecord 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 fae260a921..e7df073627 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 36c7462419..d1fb132b60 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 297792aeec..59467636d7 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -549,7 +549,7 @@ module ActiveRecord 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 7795561e51..5eb7f7e205 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/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index 5c2d8e1c1d..306bb09a0d 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -10,12 +10,22 @@ if ActiveRecord::Base.connection.supports_ranges? 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 @@ if ActiveRecord::Base.connection.supports_ranges? 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 @@ if ActiveRecord::Base.connection.supports_ranges? 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 @@ if ActiveRecord::Base.connection.supports_ranges? 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 @@ if ActiveRecord::Base.connection.supports_ranges? 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 @@ if ActiveRecord::Base.connection.supports_ranges? 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 @@ if ActiveRecord::Base.connection.supports_ranges? 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 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range, tstz_range, int4_range, - int8_range + int8_range, + float_range ) VALUES ( #{values[:id]}, '#{values[:date_range]}', @@ -237,7 +297,8 @@ if ActiveRecord::Base.connection.supports_ranges? '#{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/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 6c581a432f..38a43de05a 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -555,6 +555,24 @@ class AttributeMethodsTest < ActiveRecord::TestCase 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 diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index debacf815c..2b1749cf70 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -271,16 +271,19 @@ module ActiveRecord 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 170e9a49eb..9a4d8c6740 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -395,4 +395,22 @@ class DefaultScopingTest < ActiveRecord::TestCase 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/models/developer.rb b/activerecord/test/models/developer.rb index 2e2d8a0d37..762259ffa3 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/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 713bb3c1e2..7be22309ea 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 4eaf57f385..6afb07bd72 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 ActiveSupport # 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 @@ module ActiveSupport 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 0000000000..901c2e05a8 --- /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 b796d01dfd..9d5cee54e3 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 @@ module ActiveSupport 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 0275f4c037..aa19aed43b 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 0000000000..c200a78d36 --- /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/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb index 141f19e7b3..55f281b213 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 @@ class 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 b9d6417b07..38f726ea34 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 a74ee880b2..60bd8a06aa 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 @@ class ConcernTest < ActiveSupport::TestCase include Baz + module ClassMethods + def baz + "bar's baz + " + super + end + end + def bar "bar" end @@ -73,7 +79,7 @@ class ConcernTest < ActiveSupport::TestCase @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 0000000000..9b1fdda3b0 --- /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 c6863b24a4..07d860b71c 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 c5e2cc693a..b054a8dd31 100644 --- a/activesupport/test/core_ext/object/inclusion_test.rb +++ b/activesupport/test/core_ext/object/inclusion_test.rb @@ -48,8 +48,8 @@ class InTest < ActiveSupport::TestCase 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 4cfc5b1f10..a7c215c295 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 Binary files differindex 1918e9bf28..117a78a39f 100644 --- a/guides/assets/images/getting_started/article_with_comments.png +++ b/guides/assets/images/getting_started/article_with_comments.png diff --git a/guides/assets/images/getting_started/challenge.png b/guides/assets/images/getting_started/challenge.png Binary files differindex cc12162677..5b88a842b2 100644 --- a/guides/assets/images/getting_started/challenge.png +++ b/guides/assets/images/getting_started/challenge.png diff --git a/guides/assets/images/getting_started/confirm_dialog.png b/guides/assets/images/getting_started/confirm_dialog.png Binary files differindex e57d4b409e..9755f581a6 100644 --- a/guides/assets/images/getting_started/confirm_dialog.png +++ b/guides/assets/images/getting_started/confirm_dialog.png 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 Binary files differindex e263f7f8b2..9f32c68472 100644 --- a/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png +++ b/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png diff --git a/guides/assets/images/getting_started/form_with_errors.png b/guides/assets/images/getting_started/form_with_errors.png Binary files differindex 04ff8b1e2d..98bff37d4a 100644 --- a/guides/assets/images/getting_started/form_with_errors.png +++ b/guides/assets/images/getting_started/form_with_errors.png 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 Binary files differindex 22f994d993..0566a3ffde 100644 --- a/guides/assets/images/getting_started/index_action_with_edit_link.png +++ b/guides/assets/images/getting_started/index_action_with_edit_link.png diff --git a/guides/assets/images/getting_started/new_article.png b/guides/assets/images/getting_started/new_article.png Binary files differindex 89fc0b2605..bd3ae4fa67 100644 --- a/guides/assets/images/getting_started/new_article.png +++ b/guides/assets/images/getting_started/new_article.png diff --git a/guides/assets/images/getting_started/rails_welcome.jpg b/guides/assets/images/getting_started/rails_welcome.jpg Binary files differdeleted file mode 100644 index 65a44cdfe5..0000000000 --- a/guides/assets/images/getting_started/rails_welcome.jpg +++ /dev/null diff --git a/guides/assets/images/getting_started/rails_welcome.png b/guides/assets/images/getting_started/rails_welcome.png Binary files differnew file mode 100644 index 0000000000..3e07c948a0 --- /dev/null +++ b/guides/assets/images/getting_started/rails_welcome.png diff --git a/guides/assets/images/getting_started/routing_error_no_controller.png b/guides/assets/images/getting_started/routing_error_no_controller.png Binary files differindex ae83b6a68c..ed62862291 100644 --- a/guides/assets/images/getting_started/routing_error_no_controller.png +++ b/guides/assets/images/getting_started/routing_error_no_controller.png 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 Binary files differindex 1cbddfa0f1..08c54f921f 100644 --- a/guides/assets/images/getting_started/routing_error_no_route_matches.png +++ b/guides/assets/images/getting_started/routing_error_no_route_matches.png diff --git a/guides/assets/images/getting_started/show_action_for_articles.png b/guides/assets/images/getting_started/show_action_for_articles.png Binary files differindex 9467df6a07..4dad704f89 100644 --- a/guides/assets/images/getting_started/show_action_for_articles.png +++ b/guides/assets/images/getting_started/show_action_for_articles.png 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 Binary files differindex ba630cfc23..4e636d09ff 100644 --- a/guides/assets/images/getting_started/template_is_missing_articles_new.png +++ b/guides/assets/images/getting_started/template_is_missing_articles_new.png 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 Binary files differindex ed89c4f3d7..fd20cd53dc 100644 --- a/guides/assets/images/getting_started/unknown_action_create_for_articles.png +++ b/guides/assets/images/getting_started/unknown_action_create_for_articles.png 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 Binary files differindex e8f2b9a16a..e948a51e4a 100644 --- a/guides/assets/images/getting_started/unknown_action_new_for_articles.png +++ b/guides/assets/images/getting_started/unknown_action_new_for_articles.png diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index a16b9ac8da..3ef9e04a02 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 <http://localhost:3000>. You should see the Rails default information page: - + 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/plugins.md b/guides/source/plugins.md index 720ca5d117..fe4215839f 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/railties/CHANGELOG.md b/railties/CHANGELOG.md index bb3cc1760e..18f2546c73 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,312 +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* - -* 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/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb new file mode 100644 index 0000000000..c4f45d344b --- /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/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index d9cc60d656..b789ed9a94 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 ea3d69d7c9..87bab129bb 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 7329ee9f48..4669935156 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 @@ module Rails 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 e4fd798d18..79313c936a 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/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 01ab77ee20..bdf51b457c 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -34,6 +34,13 @@ class ModelGeneratorTest < Rails::Generators::TestCase 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 3d4e694361..55c8d92ee8 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -63,7 +63,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase 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 e4fd798d18..79313c936a 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 |