aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib
Commit message (Collapse)AuthorAgeFilesLines
* Switch to use `class_attribute`:Edouard CHIN2019-07-161-4/+3
| | | | | | | | | - Since `ActiveModel::Error` can now be inherited by `ActiveModel::NestedError`, when the latter generates a `full_message`, the `i18n_customize_full_message` accessor set in the parent class is not set. This commit fixes that by using a `class_attribute` instead.
* Move the `ActiveModel:Errors#full_message` method to the `Error` class:Edouard CHIN2019-07-164-83/+92
| | | | | | | | | | | | | | | | - One regression introduced by the "AM errors as object" features is about the `full_messages` method. It's currently impossible to call that method if the `base` object passed in the constructor of `AM::Errors` doesn't respond to the `errors` method. That's because `full_messages` now makes a weird back and forth trip `AM::Errors#full_messages` -> `AM::Error#full_message` -> `AM::Errors#full_message` Since `full_message` (singular) isn't needed by AM::Errors, I moved it to the `AM::Error` (singular) class. This way we don't need to grab the `AM::Errors` object from the base.
* Merge pull request #36654 from Edouard-chin/ec-errors-deleteRafael França2019-07-151-1/+1
|\ | | | | Returns `nil` when `AM::Errors#delete` doesn't delete anything:
| * Returns `nil` when `AM::Errors#delete` doesn't delete anything:Edouard CHIN2019-07-111-1/+1
| | | | | | | | | | | | | | | | | | - `AM::Errors#delete` currently returns an empty array when trying to delete an error that doesn't exist. This behaviour is surprising and I think it would be better to no return a truthy value but instead return nil like `Hash#delete` does.
* | Merge pull request #36649 from Edouard-chin/ec-errors-strit-match-collectionRafael França2019-07-151-2/+2
|\ \ | | | | | | Fix `AM::Errors.added?` trying to generate a message:
| * | Fix `AM::Errors.added?` trying to generate a message:Edouard CHIN2019-07-101-2/+2
| |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - When a ActiveRecord record get saved and validated as part of a collection association, the errors attribute are changed to reflect the children names. You end up with an error attribute that will look like this: `author.errors # {:'books.title' => [:blank]}` https://github.com/rails/rails/blob/2fe20cb55c76e6e50ec3a4ec5b03bbb65adba290/activerecord/lib/active_record/autosave_association.rb#L331-L340 We then can't check if the `books.title` errors was added using `ActiveModel::Errors#added?` because it tries to generate a message to make the match and end up calling the "books.title" method on the Author. ``` author.errors.added?(:'books.title', :blank) => NoMethodError: undefined method `books.title' ``` This patch modify the behaviour of `strict_match?` to not generate a message to make the comparison but instead make a strict comparison with the `options` from the error.
* | Merge pull request #36640 from Edouard-chin/ec-uniq-validation-fixRafael França2019-07-151-1/+1
|\ \ | | | | | | Fix errors getting duplicated when passed validations options:
| * | Fix errors getting duplicated when passed validations options:Edouard CHIN2019-07-101-1/+1
| |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - In 86620cc3aa8e2630bc8d934b1a86453276b9eee9, a change was made on how we remove error duplication on a record for autosave association This fix has one caveat where validation having a `if` / `unless` options passed as a proc would be considered different. Example: ```ruby class Book < ApplicationRecord has_one :author validates :title, presence: true, if -> { true } validates :title, presence: true, if -> { true } end Book.new.valid? # false Book.errors.full_messages # ["title can't be blank", "title can't be blank"] ``` While this example might sound strange, I think it's better to ignore `AM::Validations` options (if, unless ...) when making the comparison.
* / active_support/deprecation has to be already required via ↵Akira Matsuda2019-07-121-1/+0
|/ | | | active_support/rails.rb
* Update docs on dirty.rbJohn Gesimondo2019-07-051-1/+1
| | | `previously_changed` seems to actually be `previous_changes`
* Avoid redundant `time.getutc` call if it is already utc time objectRyuta Kamizono2019-06-182-5/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Currently `type.serialize` and `connection.{quote|type_cast}` for a time object always does `time.getutc` call regardless of whether it is already utc time object or not, that duplicated proccess (`connection.type_cast(type.serialize(time))`) allocates extra/useless time objects for each type casting. This avoids that redundant `time.getutc` call if it is already utc time object. In the case of a model has timestamps (`created_at` and `updated_at`), it avoids 6,000 time objects allocation for 1,000 times `model.save`. ```ruby ObjectSpace::AllocationTracer.setup(%i{path line type}) pp ObjectSpace::AllocationTracer.trace { 1_000.times { User.create } }.select { |k, _| k[0].end_with?("quoting.rb", "time_value.rb") } ``` Before (c104bfe424e6cebe9c8e85a38515327a6c88b1f8): ``` {["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 203, :T_ARRAY]=>[1004, 0, 778, 0, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 220, :T_STRING]=>[2, 0, 2, 1, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 209, :T_ARRAY]=>[8, 0, 8, 1, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 57, :T_ARRAY]=>[4, 0, 4, 1, 1, 0], ["~/rails/activemodel/lib/active_model/type/helpers/time_value.rb", 17, :T_DATA]=>[4000, 0, 3096, 0, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 120, :T_DATA]=>[2000, 0, 1548, 0, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 126, :T_STRING]=>[4000, 0, 3096, 0, 1, 0]} ``` After (this change): ``` {["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 203, :T_ARRAY]=>[1004, 0, 823, 0, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 220, :T_STRING]=>[2, 0, 2, 1, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 209, :T_ARRAY]=>[8, 0, 8, 1, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 57, :T_ARRAY]=>[4, 0, 4, 1, 1, 0], ["~/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb", 126, :T_STRING]=>[2000, 0, 1638, 0, 1, 0]} ```
* Enable `Layout/EmptyLinesAroundAccessModifier` copRyuta Kamizono2019-06-1328-31/+0
| | | | | | | | | | | We sometimes say "✂️ newline after `private`" in a code review (e.g. https://github.com/rails/rails/pull/18546#discussion_r23188776, https://github.com/rails/rails/pull/34832#discussion_r244847195). Now `Layout/EmptyLinesAroundAccessModifier` cop have new enforced style `EnforcedStyle: only_before` (https://github.com/rubocop-hq/rubocop/pull/7059). That cop and enforced style will reduce the our code review cost.
* Don't round off subseconds unless necessaryRyuta Kamizono2019-05-281-3/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Currently, if `:datetime` type has a precision, type casting always does round off subseconds regardless of whether necessary or not, it is a bit slow. Since #34970, `t.timestamps` with `precision: 6` by default, so `pluck(:created_at)` in newly created app will always be affected by the round off. This avoids the round off if possible, it makes `pluck(:created_at)` about 25% faster. https://gist.github.com/kamipo/e029539f2632aee9f5e711fe66fc8842 Before (0a87d7c9ddb95cf7568baf889ff4091469ba9af4 with postgresql adapter): ``` Warming up -------------------------------------- users.pluck(:id) 344.000 i/100ms users.pluck(:name) 336.000 i/100ms users.pluck(:created_at) 206.000 i/100ms Calculating ------------------------------------- users.pluck(:id) 3.620k (± 8.5%) i/s - 18.232k in 5.077316s users.pluck(:name) 3.579k (± 9.4%) i/s - 17.808k in 5.020230s users.pluck(:created_at) 2.069k (± 8.0%) i/s - 10.300k in 5.019284s ``` Before (0a87d7c9ddb95cf7568baf889ff4091469ba9af4 with mysql2 adapter): ``` Warming up -------------------------------------- users.pluck(:id) 245.000 i/100ms users.pluck(:name) 240.000 i/100ms users.pluck(:created_at) 180.000 i/100ms Calculating ------------------------------------- users.pluck(:id) 2.548k (± 9.4%) i/s - 12.740k in 5.066574s users.pluck(:name) 2.513k (± 8.0%) i/s - 12.480k in 5.011260s users.pluck(:created_at) 1.771k (±11.2%) i/s - 8.820k in 5.084473s ``` After (this change with postgresql adapter): ``` Warming up -------------------------------------- users.pluck(:id) 348.000 i/100ms users.pluck(:name) 357.000 i/100ms users.pluck(:created_at) 254.000 i/100ms Calculating ------------------------------------- users.pluck(:id) 3.628k (± 8.2%) i/s - 18.096k in 5.024748s users.pluck(:name) 3.624k (±12.4%) i/s - 17.850k in 5.020959s users.pluck(:created_at) 2.567k (± 7.0%) i/s - 12.954k in 5.081153s ``` After (this change with mysql2 adapter): ``` Warming up -------------------------------------- users.pluck(:id) 268.000 i/100ms users.pluck(:name) 265.000 i/100ms users.pluck(:created_at) 207.000 i/100ms Calculating ------------------------------------- users.pluck(:id) 2.586k (±10.9%) i/s - 12.864k in 5.050546s users.pluck(:name) 2.543k (±10.2%) i/s - 12.720k in 5.067726s users.pluck(:created_at) 2.263k (±14.5%) i/s - 10.971k in 5.004039s ```
* Merge pull request #36092 from imechemi/update-docGannon McGibbon2019-05-131-0/+7
|\ | | | | Improve doc for :root option in as_json()
| * Improve doc for :root option in as_json() [ci skip]Tenzin Chemi2019-05-131-0/+7
| | | | | | | | | | | | | | | | Remove trailing whitespace [ci skip] Reword Root value should be string [ci skip]
* | Recover perf for `pluck` by reverting 9c9c950d02af83742a5f76302d0faa99508f242c.Guo Xiang Tan2019-05-031-0/+5
| | | | | | | | This reverts commit 9c9c950d02af83742a5f76302d0faa99508f242c.
* | any? should be delegated to the errors listAaron Patterson2019-04-301-1/+1
|/ | | | Otherwise we get deprecation warnings in the generated scaffold template files
* Change the deprecation for Enumerating ActiveModel::Errors to Rails 6.1 ↵Abhay Nikam2019-04-251-5/+5
| | | | | | | | instead of 6.0 (#36087) * Change the deprecation for Enumerating ActiveModel::Errors to Rails 6.1 instead of 6.0 * Changed the deprecation message for ActiveModel::Errors methods: slice, values, keys and to_xml
* Merge pull request #32313 from lulalala/model_error_as_objectRafael França2019-04-244-118/+355
|\ | | | | Model error as object
| * Set default array to detailslulalala2019-03-311-1/+9
| | | | | | | | maintaining behavior errors.details[:foo].any?
| * Fix messages[]= does not override valuelulalala2019-03-311-0/+1
| |
| * Freeze DeprecationHandling array and hashlulalala2019-03-311-13/+20
| |
| * Fix equality comparison raising error buglulalala2019-03-311-1/+1
| |
| * Fix speclulalala2019-03-311-1/+1
| |
| * Split messages and to_hashlulalala2019-03-311-18/+21
| | | | | | | | | | | | Fix double wrapping issue Revert messages_for wrapping. It's a new method so no need to put deprecation warnings.
| * Raise deprecation for calling `[:f] = 'b'` or `[:f] << 'b'`lulalala2019-03-311-4/+46
| | | | | | | | Revert some tests to ensure back compatibility
| * Add deprecation to slice!lulalala2019-03-311-0/+2
| |
| * Nested attribute error's attribute name to use different key:lulalala2019-03-311-1/+1
| | | | | | | | To keep the same as SHA dcafe995bfe51e53dd04607956be9b54073e9cb6
| * String override options in #import to convert to symbollulalala2019-03-311-0/+5
| |
| * Allow errors to remove duplicates, and ensure cyclic associations w/ ↵lulalala2019-03-312-1/+16
| | | | | | | | | | autosave duplicate errors can be removed See SHA 7550f0a016ee6647aaa76c0c0ae30bebc3867288
| * Add messages_forlulalala2019-03-311-1/+7
| |
| * Add a transitional method `objects`, for accessing the array directly.lulalala2019-03-311-0/+1
| | | | | | | | This is because we try to accommodate old hash behavior, so `first` and `last` now does not return Error object.
| * Backward compatibility for errors.collect/select etc.lulalala2019-03-311-2/+2
| | | | | | | | | | All enumerable methods must go through the `each` so it retain old hash behavior. Revert this after Rails 6.1 in order to speed up enumerable methods.
| * Add convenience method group_by_attributelulalala2019-03-311-20/+9
| | | | | | | | | | | | Many operations need grouping of errors by attributes, e.g. ActiveRecord::AutosaveAssociation#association_valid? Refactor other methods using group_by_attribute
| * Change errorslulalala2019-03-312-109/+190
| | | | | | | | | | | | | | | | | | | | | | | | Allow `each` to behave in new way if block arity is 1 Ensure dumped marshal from Rails 5 can be loaded Make errors compatible with marshal and YAML dumps from previous versions of Rails Add deprecation warnings Ensure each behave like the past, sorted by attribute
| * Add ActiveModel::Error and NestedErrorlulalala2019-03-313-16/+93
| | | | | | | | | | | | | | Add initialize_dup to deep dup. Move proc eval and flexible message position out to Errors, because proc eval is needed for Errors#added? and Errors#delete
* | Start Rails 6.1 developmentRafael Mendonça França2019-04-241-2/+2
| |
* | Avoid redundant `attribute_alias?` before `attribute_alias`Ryuta Kamizono2019-04-241-10/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If we want to get alias resolved attribute finally, we can use `attribute_alias` directly. For that purpose, avoiding redundant `attribute_alias?` makes alias attribute access 40% faster. https://gist.github.com/kamipo/e427f080a27b46f50bc508fae3612a0e Before (2c0729d8): ``` Warming up -------------------------------------- user['id'] 102.668k i/100ms user['new_id'] 80.660k i/100ms user['name'] 99.368k i/100ms user['new_name'] 81.626k i/100ms Calculating ------------------------------------- user['id'] 1.431M (± 4.0%) i/s - 7.187M in 5.031985s user['new_id'] 1.042M (± 4.2%) i/s - 5.243M in 5.039858s user['name'] 1.406M (± 5.6%) i/s - 7.055M in 5.036743s user['new_name'] 1.074M (± 3.6%) i/s - 5.387M in 5.024152s ``` After (this change): ``` Warming up -------------------------------------- user['id'] 109.775k i/100ms user['new_id'] 103.303k i/100ms user['name'] 105.988k i/100ms user['new_name'] 99.618k i/100ms Calculating ------------------------------------- user['id'] 1.520M (± 6.7%) i/s - 7.574M in 5.011496s user['new_id'] 1.485M (± 6.2%) i/s - 7.438M in 5.036252s user['name'] 1.538M (± 5.4%) i/s - 7.737M in 5.049765s user['new_name'] 1.516M (± 4.6%) i/s - 7.571M in 5.007293s ```
* | Merge pull request #36061 from shioyama/update_commentRafael França2019-04-231-2/+3
|\ \ | | | | | | Update comment in attribute_method_matchers_matching
| * | Update comment in attribute_method_matchers_matchingChris Salzberg2019-04-231-2/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The current comment here is from 2011 and its original context has changed completely. The plain matcher will not "match every time" anymore since the code now filters all matchers, and only selects those for which the captured attribute is valid. To avoid confusion, I updated the comment. For more discussion, see: https://github.com/rails/rails/pull/36005
* | | Add attribute_names to ActiveModel::AttributesDaniel Colson2019-04-221-0/+44
|/ / | | | | | | | | | | | | | | | | | | | | | | | | | | | | This adds `.attribute_names` and `#attribute_names` to `ActiveModel::Attributes` along the same lines as the corresponding methods in `ActiveRecord::AttributeMethods` (see [`.attribute_names`][class_method] and [`#attribute_names`][instance_method]. While I was here I also added documentation for '#attributes', which I added in 043ce35b186. The whole class is still `#:nodoc:` so I don't think this will have any effect for now. [class_method]: https://github.com/rails/rails/blob/cc834db1d0815744cfa173813c05d928e008e167/activerecord/lib/active_record/attribute_methods.rb#L154-L160 [instance_method]: https://github.com/rails/rails/blob/cc834db1d0815744cfa173813c05d928e008e167/activerecord/lib/active_record/attribute_methods.rb#L299-L301
* | Improve wording of commentsChris Salzberg2019-04-132-8/+8
| | | | | | | | | | | | Most of the time, these methods are called from actual methods defined from columns in the schema, not from method_missing, so the current wording is misleading.
* | Rename method_missing_target to targetChris Salzberg2019-04-131-10/+10
| | | | | | | | | | | | | | | | | | | | | | The target of matchers is used in two contexts: to define attribute methods which dispatch to handlers like attribute_was, and to match method calls in method_missing and dispatch to those same handler methods. Only in the latter context does the term "method_missing_target" make any sense; in the former context it is just confusing. "target" is not ideal as a term but at least it avoids this confusion.
* | Remove unused method_name from AttributeMethodMatchChris Salzberg2019-04-121-2/+2
| |
* | Rename "method" to "matcher" in map blockChris Salzberg2019-04-121-1/+1
| |
* | PERF: 2x ~ 30x faster dirty trackingRyuta Kamizono2019-04-112-120/+116
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Currently, although using both dirty tracking (ivar backed and attributes backed) on one model is not supported (doesn't fully work at least), both dirty tracking are being performed, that is very slow. As long as attributes backed dirty tracking is used, ivar backed dirty tracking should not need to be performed. I've refactored to extract new `ForcedMutationTracker` which only tracks `force_change` to be performed for ivar backed dirty tracking, that makes dirty tracking on Active Record 2x ~ 30x faster. https://gist.github.com/kamipo/971dfe0891f0fe1ec7db8ab31f016435 Before: ``` Warming up -------------------------------------- changed? 4.467k i/100ms changed 5.134k i/100ms changes 3.023k i/100ms changed_attributes 4.358k i/100ms title_change 3.185k i/100ms title_was 3.381k i/100ms Calculating ------------------------------------- changed? 42.197k (±28.5%) i/s - 187.614k in 5.050446s changed 50.481k (±16.0%) i/s - 246.432k in 5.045759s changes 30.799k (± 7.2%) i/s - 154.173k in 5.030765s changed_attributes 51.530k (±14.2%) i/s - 252.764k in 5.041106s title_change 44.667k (± 9.0%) i/s - 222.950k in 5.040646s title_was 44.635k (±16.6%) i/s - 216.384k in 5.051098s ``` After: ``` Warming up -------------------------------------- changed? 24.130k i/100ms changed 13.503k i/100ms changes 6.511k i/100ms changed_attributes 9.226k i/100ms title_change 48.221k i/100ms title_was 96.060k i/100ms Calculating ------------------------------------- changed? 245.478k (±16.1%) i/s - 1.182M in 5.015837s changed 157.641k (± 4.9%) i/s - 796.677k in 5.066734s changes 70.633k (± 5.7%) i/s - 358.105k in 5.086553s changed_attributes 95.155k (±13.6%) i/s - 470.526k in 5.082841s title_change 566.481k (± 3.5%) i/s - 2.845M in 5.028852s title_was 1.487M (± 3.9%) i/s - 7.493M in 5.046774s ```
* | Refactor `has_secure_password` to extract dedicated attribute moduleRyuta Kamizono2019-04-051-36/+38
| | | | | | | | | | | | Follow up of #26764 and #35700. And add test case for #35700.
* | Merge pull request #35700 from Futurelearn/seb-secure-password-fixRyuta Kamizono2019-04-051-26/+30
|\ \ | |/ |/| Reintroduce support for overriding `has_secure_password` attributes
| * Reintroduce support for overriding `has_secure_password` attributesSeb Jacobs2019-03-221-26/+30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In Rails 5.2.x calling `has_secure_password` would define attribute readers and writers on the superclass of the model, which meant that you could override these attributes in a model and call the superclass for example: ``` class Dog < ApplicationRecord has_secure_password def password=(new_password) @password_set = new_password.present? super end end ``` However this behaviour was broken in Rails 6 when the ability to customise the name of the attribute was introduced [1] since they are no longer being defined on the superclass you will now see the following error: ``` NoMethodError: super: no superclass method `password=' for #<Dog:0x00007ffbbc7ce290> Did you mean? password ``` In order to resolve this issue and retain support for setting a custom attribute name we can define these attribute readers/writers in a module and then ensure that the module is included in the inheritance chain. [1] https://www.github.com/rails/rails/commit/86a48b4da3 https://www.github.com/rails/rails/commit/9b63bf1dfd
* | Merge pull request #35794 from kamipo/type_cast_symbol_falseRyuta Kamizono2019-03-301-1/+10
|\ \ | | | | | | Type cast falsy boolean symbols on boolean attribute as false