Commit Graph

2646 Commits

Author SHA1 Message Date
Fabio Sangiovanni
12f3da0ac3 Fix punctuation in has_secure_password docs. 2022-08-30 19:32:00 +02:00
Jacopo
2f5136a3be Normalize virtual attributes on ActiveRecord::Persistence#becomes
When source and target classes have a different set of attributes adapts
attributes such that the extra attributes from target are added.

Fixes #41195

Co-authored-by: SampsonCrowley <sampsonsprojects@gmail.com>
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2022-08-24 22:51:57 +02:00
Andrew Novoselac
7519457678 Fixes a defect in ActiveModel::Dirty#attribute_changed? where the :from
and :to options are not being type cast. For example, for an enum
attribute, attribute_changed? should handle a String, Symbol or Integer
for the :from and :to options.
2022-08-05 11:52:20 -04:00
Jonathan Hefner
239d6fa67d Use tap in conditionally-required password example [ci-skip] 2022-08-04 11:39:29 -05:00
Jonathan Hefner
f53e3ad938 Add example of conditionally requiring a password [ci-skip]
This example addresses the use case brought up by #45487, which has been
reverted by #45753.
2022-08-03 16:14:08 -05:00
Jonathan Hefner
6020f5331d Revert "Allow passing hash on secure password validations"
This reverts #45487 (8804128ad4cfea228ce97005072b029d0379d80f) until a
better API can be decided upon.
2022-08-03 11:52:30 -05:00
Kevin Jacoby
8804128ad4 Allow passing hash on secure password validations
I was running into a case where I didn't want to just disabled the
validations and add my own. In fact, I would very much like to keep the
default validation but just de-activate it on some scenario:
e.g. Inviting a user without having to set a password for them yet so
they can add it themselves later when they receive an email invitation
to finish setting up their account.

My understanding of the validations flag originally intended was to
just disabled them and if you needed something more custom, you could
run your own validations instead.

This would be an acceptable solution, but it would add more code to my
controller. Instead validations can receive a `Hash` wich is then use to
apply validations rules to `validate`.

This is just a suggestion, I am not sure if there is a need, and I am
aware this PR is probably far from perfect. Any feedback welcome.

EDIT: implemented changes as per feedback.
2022-07-26 10:11:43 -05:00
Jonathan Hefner
71c0c81b47 Add CHANGELOG entry for #43688 [ci-skip] 2022-07-25 12:16:46 -05:00
fatkodima
3822172c00 Fix caching of missed translations 2022-07-15 14:49:11 -05:00
Ryuta Kamizono
c3fd9f1776 Remove reverted CHANGELOG entry [ci-skip]
Follow up to #45553.
2022-07-10 10:02:27 +09:00
Gannon McGibbon
17b4b8fd63 Revert "Provide pattern matching for ActiveModel"
This reverts commit 7e499b25acd5e1bfdd54ca2af66678b0ed05def1.
2022-07-09 00:13:23 -04:00
Jean Boussier
01f0cea3b5 Fix code cache namespacing for proxied attribute methods
Fix: https://github.com/rails/rails/issues/45416

The suffix need to be included in the namespace as it is used to
generate the code.
2022-06-25 09:02:41 +02:00
Shouichi Kamiya
f2695d2dc0 Use const_get to get validator classes
Though we needed to use constantize for ruby < 2.0, ruby >= 2.0
const_get can handle namespace.

> https://docs.ruby-lang.org/en/2.0.0/NEWS.html
> Module#const_get accepts a qualified constant string, e.g. Object.const_get(“Foo::Bar::Baz”)
2022-06-01 17:14:45 +09:00
Jonathan Hefner
0fb166e81c Improve performance for contextual validations
This change reduces retained memory when registering contextual
validations, and reduces overhead when running contextual validations.

Benchmark script:

```ruby
require "benchmark/memory"
require "benchmark/ips"

class Post
  include ActiveModel::Validations

  def foo; end
  def bar; end
end

class Comment
  include ActiveModel::Validations

  def foo; end
  def bar; end
end

Benchmark.memory do |x|
  x.report("warmup") do
    Post.validate :will_not_be_called, on: :warmup
    Comment.validate :will_not_be_called, on: :warmup
  end

  x.report("register validations") do
    Post.validate :foo, on: :create
    Post.validate :bar, on: :update
    Comment.validate :foo, on: :create
    Comment.validate :bar, on: :update
  end
end

post = Post.new

Benchmark.ips do |x|
  x.report("run validations") do
    post.valid?(:create)
  end
end
```

Before:

```
Calculating -------------------------------------
              warmup     3.320k memsize (     1.552k retained)
                        48.000  objects (    21.000  retained)
                         0.000  strings (     0.000  retained)
register validations     5.856k memsize (     2.456k retained)
                        92.000  objects (    33.000  retained)
                         0.000  strings (     0.000  retained)
Warming up --------------------------------------
     run validations     6.232k i/100ms
Calculating -------------------------------------
     run validations     61.826k (± 0.7%) i/s -    311.600k in   5.040176s
```

After:

```
Calculating -------------------------------------
              warmup     3.960k memsize (     1.400k retained)
                        51.000  objects (    17.000  retained)
                         0.000  strings (     0.000  retained)
register validations     6.368k memsize (     1.384k retained)
                        94.000  objects (    21.000  retained)
                         0.000  strings (     0.000  retained)
Warming up --------------------------------------
     run validations     6.998k i/100ms
Calculating -------------------------------------
     run validations     70.741k (± 0.6%) i/s -    356.898k in   5.045347s
```
2022-05-27 15:37:11 -05:00
Jonathan Hefner
5389c56292 Dup options in validates_with
Some validators, such as validators that inherit from `EachValidator`,
mutate the options they receive.  This can cause problems when passing
multiple validators and options to `validates_with`.  This can also be a
problem if a validator deletes standard options such as `:if` and `:on`,
because the validation callback would then not receive them.

This commit modifies `validates_with` to `dup` options before passing
them to validators, thus preventing these issues.

Fixes #44460.
Closes #44476.

Co-authored-by: Dieter Späth <dieter.spaeth@lanes-planes.com>
2022-05-26 15:08:00 -05:00
fatkodima
91cd5cbc93 Support infinite ranges for LengthValidators :in/:within options 2022-05-20 20:41:18 +03:00
Bo Jeanes
28e40a6405 Add beginless range support to clusivity 2022-05-20 07:46:01 +10:00
fatkodima
00b269ec8d Make validators accept lambdas without record argument 2022-05-18 19:53:29 +03:00
Kevin Newton
7e499b25ac
Provide pattern matching for ActiveModel
It would be nice to be able to pattern match against ActiveModel (and
transitively ActiveRecord). If you want to check multiple attributes
with conditions, it's nice to be able use the pattern matching syntax.

For example:

```ruby
case Current.user
in { superuser: true }
  "Thanks for logging in. You are a superuser."
in { admin: true, name: }
  "Thanks for logging in, admin #{name}!"
in { name: }
  "Welcome, #{name}!"
end
```
2022-05-11 12:47:59 -04:00
fatkodima
0f4b030c44 Fix casting long strings to Date, Time or DateTime 2022-05-11 16:40:31 +03:00
Ryuta Kamizono
714fd07fd9 All intermediate delegation methods should preserve kwargs flag
Since 0456826180,
`foo(*caller_args)` method call delegation no longer preserve kwargs
flag.

Fixes #44846.
2022-04-06 15:32:54 +09:00
Petrik
8e91abbea1 Remove confusing validations comments [ci-skip]
All validations are called on `#save` by default.
The Absence, Acceptance, Presence validators don't do anything special,
so there is no reason to mention they are called on `#save` by default.

Instead we can mention all validations are called in `#save` by default
in ActiveRecord::Validations.

Co-authored-by: Gannon McGibbon <gannon@hey.com>
2022-03-29 16:04:20 +02:00
Kevin Dew
322e62842c
Remove override of ActiveModel#attribute_names
In https://github.com/rails/rails/pull/43036 an optimisation was applied
to ActiveModel#Serialization to speed up the generation of a
serialized_hash by avoiding loading the subjects attributes by using an
attribute_names method. A fallback method,
ActiveModel::Serialization#attribute_names` was added as #attribute_names
isn't part of the public API of ActiveModel.

Unfortunately, this fallback method happens to override the ActiveRecord
method (as ActiveModel::Serialization is a later mixin than
ActiveRecord::AttributeMethods), so this change didn't actually provide
an optimisation - the full attribute data was loaded as per [1]

This change also, in our case, produced some severe performance issues
as it introduced an N+1 query in a situation where we had one gem,
Globalize [2], which adds in dynamic attributes that are loaded by a query;
and another gem, Transitions [3], that checks attribute names at
initialization. The combination of these meant that for every model that
was initialized an extra query would run - no matter what includes or
eager_load steps were in place. This rapidly hindered our applications'
performance and meant we had to rollback the Rails 7 upgrade.

Following rafaelfranca's suggestion [4] this adds a
`attribute_names_for_serialization` method to Serialization modules in
ActiveRecord and ActiveModel. This allows the ActiveRecord one to
override the ActiveModel fallback and thus be optimised.

Some basic benchmarks of this follow - they use code from
https://github.com/ollietreend/rails-demo and have some pretty large
arrays set as serialized attributes [5] to demonstrate impacts.

Loading attribute names:

Rails 7.0.2.3

```
> Benchmark.ms { Widget.all.map(&:attribute_names) }
  Widget Load (131.1ms)  SELECT "widgets".* FROM "widgets"
=> 20108.852999983355
```

This patch

```
> Benchmark.ms { Widget.all.map(&:attribute_names) }
  Widget Load (144.0ms)  SELECT "widgets".* FROM "widgets"
=> 237.96699999365956
```

Using serializable_hash:

Rails 7.0.2.3

```
> widgets = Widget.all.to_a; Benchmark.ms { widgets.map { |w| w.serializable_hash(only: []) } }
  Widget Load (133.3ms)  SELECT "widgets".* FROM "widgets"
=> 22071.45000001765
```

This patch

```
> widgets = Widget.all.to_a; Benchmark.ms { widgets.map { |w| w.serializable_hash(only: []) } }
  Widget Load (83.5ms)  SELECT "widgets".* FROM "widgets"
=> 67.9039999959059
```

[1]: eeb2cfb686/activemodel/lib/active_model/serialization.rb (L151-L154)
[2]: https://github.com/globalize/globalize
[3]: https://github.com/troessner/transitions
[4]: https://github.com/rails/rails/pull/44770#pullrequestreview-922209612
[5]: 525f88887b/db/seeds.rb
2022-03-26 10:31:11 +00:00
Aaron Patterson
4be60c9cbd
Merge pull request #44664 from jonathanhefner/active_model-attribute_registration
Factor out `ActiveModel::AttributeRegistration`
2022-03-17 10:24:11 -07:00
Rafael Mendonça França
5c1bd20f0d
Merge pull request #44693 from ghousemohamed/fix-docs-related-gem-versions
Fix `#version` method docs and some typos [ci-skip]
2022-03-15 16:28:07 -04:00
Ghouse Mohamed
6ee6cb554b Fix #version docs and some typos 2022-03-16 01:48:37 +05:30
Jonathan Hefner
608cbfae36 Factor out ActiveModel::AttributeRegistration
As a step toward sharing more code between Active Model and Active
Record, this commit factors an `ActiveModel::AttributeRegistration`
module out of `ActiveModel::Attributes`.  This module is marked as
`nodoc` and is for internal use only.

Additionally, this commit adds thorough test coverage of attribute
registration and inheritance.  (`activemodel/test/cases/attributes_test.rb`
does already test some of this behavior, but it is focused on high-level
functionality.)
2022-03-11 22:15:14 -06:00
Jonathan Hefner
13cfc27c7b Support password challenge via has_secure_password
This enhances `has_secure_password` to define a `password_challenge`
accessor and the appropriate validation.  When `password_challenge` is
set, the validation checks that it matches the currently *persisted*
`password_digest` (i.e. `password_digest_was`).

This allows a password challenge to be implemented with the same ease as
a password confirmation, re-using the same error handling logic in the
view, as well as the controller.  For example, in the controller,
instead of:

```ruby
password_params = params.require(:password).permit(
  :password_challenge,
  :password,
  :password_confirmation,
)

password_challenge = password_params.delete(:password_challenge)
@password_challenge_failed = !current_user.authenticate(password_challenge)

if !@password_challenge_failed && current_user.update(password_params)
  # ...
end
```

One could write:

```ruby
password_params = params.require(:password).permit(
  :password_challenge,
  :password,
  :password_confirmation,
).with_defaults(password_challenge: "")

if current_user.update(password_params)
  # ...
end
```

And, in the view, instead of checking `@password_challenge_failed`, one
could render a password challenge error in the same manner as other form
field errors, including utilizing `config.action_view.field_error_proc`.
2022-02-28 12:17:02 -06:00
Rafael Mendonça França
269037a765
Merge pull request #44501 from ghousemohamed/fix-inconsistency-in-activemodel-testcases
Fixed inconsistencies in ActiveModel test cases
2022-02-25 14:53:02 -05:00
Jonathan Hefner
de5a4b3ab8 Add ActiveModel::Access
This ports `ActiveRecord::Base#slice` and `#values_at` to a new
`ActiveModel::Access` module, which is included in `ActiveModel::Model`
by default.
2022-02-24 13:52:21 -06:00
Ghouse Mohamed
22fe46b40f Fixed inconsistencies in ActiveModel test cases 2022-02-23 01:30:26 +05:30
Jonathan Hefner
a199aaedb8 Cross-link API docs [ci-skip]
RDoc will automatically format and link API references as long as they
are not already marked up as inline code.

This commit removes markup from various API references so that those
references will link to the relevant API docs.
2022-02-21 11:45:25 -06:00
Jonathan Hefner
9dbf7a58a2 Fix formatting of parameters doc [ci-skip] 2022-02-21 11:11:11 -06:00
Jonathan Hefner
e37adfed4e Add Oxford commas [ci-skip] 2022-02-21 11:11:11 -06:00
Jonathan Hefner
0d3effc97e Replace "overwrite" with "override" [ci-skip]
"Overwrite" means "destructively replace", and is more suitable when,
for example, talking about writing data to a location.

"Override" means "supersede", and is more suitable when, for example,
talking about redifining methods in a subclass.
2022-02-21 11:11:11 -06:00
Jonathan Hefner
5fdbd217d1 Fix typos [ci-skip] 2022-02-21 11:11:11 -06:00
Usman
3324cbdff5 Update ActiveModel::Attributes examples in docs 2022-02-11 01:19:49 +05:00
Jonathan Hefner
61468079b6
Merge pull request #44306 from volmer/attributes-docs
Documentation for Active Model Attributes [ci-skip]
2022-02-09 14:33:09 -06:00
Volmer Campos Soares
07a5584999 Documentation for Active Model Attributes
This commit adds documentation to the constants and methods that are
part of Active Model's Attributes API. So far this API has been hidden
with the :nodoc: flag since its inception in Active Record and
subsequent move to Active Model (#30920 and #30985); as the API matures
and gets ready for public usage, visible documentation for its endpoints
becomes necessary.

The classes and modules being documented and publicized by this commit
are the main `Attributes` module, the `Type` namespace, and all the
standard attribute type classes included in the current API, which users
will be able to extend and replicate to suit their customization needs.

Some private modules are also receiving documetation, although they will
continue using the :nodoc: flag. Those are `Attribute` and
`AttributeSet`. Although they remain private I found useful to add some
comments to describe their responsibilities.
2022-02-09 14:17:18 -05:00
Chris Salzberg
fd415ef98d Rename target -> proxy_target 2022-02-09 10:55:10 +09:00
Chris Salzberg
251445601e Rename AttributeMethodMatcher to AttributeMethodPattern 2022-02-09 10:41:02 +09:00
David Heinemeier Hansson
41478f7074 Make #to_fs the default replacement for #to_s(:format)
#to_formatted_s is too cumbersome.
2022-02-07 12:41:21 +01:00
Ryuta Kamizono
ed4d81caf7
Merge pull request #44299 from jonathanhefner/model_name-human-performance
Improve `ActiveModel::Name#human` performance
2022-02-04 07:58:17 +09:00
Chris Salzberg
81519dec1a Use different namespace for proxy calls 2022-02-03 10:10:07 +01:00
Jonathan Hefner
acbc39b663 Improve human_attribute_name performance
This reduces allocations and improves performance by ~35% when a
translation is defined and ~50% when a translation is not defined.

Benchmark script:

```ruby
require "benchmark/memory"
require "benchmark/ips"

class BaseModel
  extend ActiveModel::Translation
end

Person = Class.new(BaseModel)

module A
  Person = Class.new(BaseModel)

  module B
    Person = Class.new(BaseModel)
  end
end

I18n.backend.store_translations "en",
  activemodel: { attributes: { person: { has_translation: "translated" } } }

Benchmark.memory do |x|
  x.report("warmup") do
    Person.human_attribute_name("first_name")
    A::Person.human_attribute_name("first_name")
    A::B::Person.human_attribute_name("first_name")
    Person.human_attribute_name("has_translation")
  end

  x.report("no namespace") { Person.human_attribute_name("first_name") }
  x.report("1 namespace")  { A::Person.human_attribute_name("first_name") }
  x.report("2 namespaces") { A::B::Person.human_attribute_name("first_name") }

  x.report("has translation") { Person.human_attribute_name("has_translation") }
end

Benchmark.ips do |x|
  x.report("no namespace") { Person.human_attribute_name("first_name") }
  x.report("1 namespace")  { A::Person.human_attribute_name("first_name") }
  x.report("2 namespaces") { A::B::Person.human_attribute_name("first_name") }

  x.report("has translation") { Person.human_attribute_name("has_translation") }
end
```

Before:

```
Calculating -------------------------------------
              warmup   988.923k memsize (    24.587k retained)
                         2.441k objects (   339.000  retained)
                        50.000  strings (    50.000  retained)
        no namespace     6.416k memsize (     0.000  retained)
                        58.000  objects (     0.000  retained)
                        18.000  strings (     0.000  retained)
         1 namespace     6.416k memsize (     0.000  retained)
                        58.000  objects (     0.000  retained)
                        18.000  strings (     0.000  retained)
        2 namespaces     6.416k memsize (     0.000  retained)
                        58.000  objects (     0.000  retained)
                        18.000  strings (     0.000  retained)
     has translation     4.501k memsize (     0.000  retained)
                        46.000  objects (     0.000  retained)
                        18.000  strings (     0.000  retained)

Warming up --------------------------------------
        no namespace   567.000  i/100ms
         1 namespace   563.000  i/100ms
        2 namespaces   565.000  i/100ms
     has translation   839.000  i/100ms
Calculating -------------------------------------
        no namespace      5.642k (± 0.9%) i/s -     28.350k in   5.025255s
         1 namespace      5.652k (± 0.9%) i/s -     28.713k in   5.080325s
        2 namespaces      5.662k (± 1.1%) i/s -     28.815k in   5.090226s
     has translation      8.391k (± 1.6%) i/s -     42.789k in   5.100484s
```

After:

```
Calculating -------------------------------------
              warmup   982.803k memsize (    24.587k retained)
                         2.385k objects (   339.000  retained)
                        50.000  strings (    50.000  retained)
        no namespace     4.712k memsize (     0.000  retained)
                        44.000  objects (     0.000  retained)
                        13.000  strings (     0.000  retained)
         1 namespace     4.712k memsize (     0.000  retained)
                        44.000  objects (     0.000  retained)
                        12.000  strings (     0.000  retained)
        2 namespaces     4.712k memsize (     0.000  retained)
                        44.000  objects (     0.000  retained)
                        12.000  strings (     0.000  retained)
     has translation     3.493k memsize (     0.000  retained)
                        32.000  objects (     0.000  retained)
                        11.000  strings (     0.000  retained)

Warming up --------------------------------------
        no namespace   850.000  i/100ms
         1 namespace   846.000  i/100ms
        2 namespaces   842.000  i/100ms
     has translation     1.127k i/100ms
Calculating -------------------------------------
        no namespace      8.389k (± 0.9%) i/s -     42.500k in   5.066296s
         1 namespace      8.412k (± 0.6%) i/s -     42.300k in   5.028401s
        2 namespaces      8.423k (± 0.6%) i/s -     42.942k in   5.098322s
     has translation     11.303k (± 1.1%) i/s -     57.477k in   5.085568s
```
2022-02-02 11:50:29 -06:00
Jonathan Hefner
b2f49f27ad Improve ActiveModel::Name#human performance
This refactors `ActiveModel::Name#human` to reduce allocations and
improve performance by ~2x when a translation is not defined.

Benchmark script:

```ruby
require "benchmark/memory"
require "benchmark/ips"

class BaseModel
  extend ActiveModel::Translation
end

BlogPost = Class.new(BaseModel)

Benchmark.memory do |x|
  x.report("warmup") { BlogPost.model_name.human }
  x.report("human")  { BlogPost.model_name.human }
end

Benchmark.ips do |x|
  x.report("human")  { BlogPost.model_name.human }
end
```

Before:

```
Calculating -------------------------------------
              warmup   964.242k memsize (    23.575k retained)
                         2.157k objects (   310.000  retained)
                        50.000  strings (    50.000  retained)
               human     4.144k memsize (     0.000  retained)
                        32.000  objects (     0.000  retained)
                         6.000  strings (     0.000  retained)

Warming up --------------------------------------
               human   870.000  i/100ms
Calculating -------------------------------------
               human      8.434k (± 0.8%) i/s -     42.630k in   5.054943s
```

After:

```
Calculating -------------------------------------
              warmup   962.418k memsize (    23.607k retained)
                         2.143k objects (   310.000  retained)
                        50.000  strings (    50.000  retained)
               human     2.112k memsize (     0.000  retained)
                        16.000  objects (     0.000  retained)
                         1.000  strings (     0.000  retained)

Warming up --------------------------------------
               human     1.851k i/100ms
Calculating -------------------------------------
               human     18.492k (± 0.7%) i/s -     92.550k in   5.005100s
```
2022-02-02 11:11:56 -06:00
Ryuta Kamizono
65766ebcc8 Bump license years to 2022 [ci-skip] 2022-01-01 15:22:15 +09:00
Orhan Toy
318d6905e2 Add only_numeric option to numericality validator 2021-12-16 22:21:14 +01:00
Rafael Mendonça França
83d85b2207
Start Rails 7.1 development 2021-12-07 15:52:30 +00:00
Rafael Mendonça França
c2e12e0191
Use to_formatted_s(:db) instead of to_s(:db) internally
Ruby 3.1 introduced an optimization to string interpolation for some
core classes in b08dacfea3.

But since we override `to_s` in some of those core classes to add behavior
like `to_s(:db)`, all Rails applications will not be able to take advantage
of that improvement.

Since we can use the `to_formatted_s` alias for the Rails specific behavior
it is best for us to deprecate the `to_s` core extension and allow Rails
applications to get the proformace improvement.

This commit starts removing all the `to_s(:db)` calls inside the framework
so we can deprecate the core extension in the next commit.
2021-12-06 19:22:04 +00:00
Rafael Mendonça França
6f2c4027e8
Use assert_equal 2021-11-29 22:49:39 +00:00
Zoran Pesic
1de55c3596
don't import errors if attempting to merge with self
Prior to this change, attempting to merge an `ActiveModel::Error` instance
with itself would result in an endless loop where `ActiveModel::NestedError`s
would continue to be imported on the instance until interrupted. Though the
merging of identical objects is less likely to happen in practice, this method
should still be able to handle such a case gracefully. This change ensures
that instances attempting to merge on themselves return early rather than
hanging indefinitely.

Addresses https://github.com/rails/rails/issues/43737
2021-11-29 10:17:36 -08:00
Rafael Mendonça França
902e829914
Merge pull request #43284 from mibradev/password-digest-nil
Prevent error when authenticating user with a blank password digest
2021-11-25 13:59:42 -05:00
Rafael Mendonça França
ad96027f4c
Make sure errors.messages works in the same way as Rails 6.1
When there are no errors for a given attribute we were returning empty
arrays. We should continue to do that.
2021-11-25 18:15:27 +00:00
Rafael Mendonça França
50ec25b506
Remove duplicated tests 2021-11-19 23:15:59 +00:00
Rafael Mendonça França
1f3cfb272c
Remove support to Marshal load Rails 5.x ActiveModel::AttributeSet format 2021-11-17 21:51:32 +00:00
Rafael Mendonça França
2996732089
Remove support to Marshal and YAML load Rails 5.x error format 2021-11-17 21:51:31 +00:00
Rafael Mendonça França
70dc990ad2
Remove deprecated support to use []= in ActiveModel::Errors#messages 2021-11-17 21:51:30 +00:00
Rafael Mendonça França
bab78b5d54
Remove deprecated support delete errors from ActiveModel::Errors#messages 2021-11-17 21:51:29 +00:00
Rafael Mendonça França
b2db6f390c
Implement each using a delegator and make sure all enumerable methods are available 2021-11-17 21:51:28 +00:00
Rafael Mendonça França
93edfaa7b4
Remove unnecessary class 2021-11-17 21:51:27 +00:00
Rafael Mendonça França
ef40a92c1c
Remove deprecated support clear errors from ActiveModel::Errors#messages 2021-11-17 21:51:26 +00:00
Rafael Mendonça França
884c97fad0
Remove deprecated support concat errors to ActiveModel::Errors#messages 2021-11-17 21:51:25 +00:00
Rafael Mendonça França
8a5e217b47
Remove unused method 2021-11-17 21:51:24 +00:00
Rafael Mendonça França
73872c7220
Remove deprecated ActiveModel::Errors#to_xml 2021-11-17 21:51:23 +00:00
Rafael Mendonça França
edc4e7dfb5
Remove deprecated ActiveModel::Errors#keys 2021-11-17 21:51:22 +00:00
Rafael Mendonça França
6fed53b694
Remove deprecated ActiveModel::Errors#values 2021-11-17 21:51:21 +00:00
Rafael Mendonça França
362e17e899
Remove deprecated ActiveModel::Errors#slice! 2021-11-17 21:51:20 +00:00
Rafael Mendonça França
a743656ae4
Remove deprecated ActiveModel::Errors#to_h 2021-11-17 21:51:19 +00:00
Rafael Mendonça França
05b18d2694
Remove deprecated enumeration of ActiveModel::Errors instances as a Hash 2021-11-17 21:51:18 +00:00
Rafael Mendonça França
1fde031e89 Fix gemspec 2021-11-15 21:06:21 +00:00
Rafael Mendonça França
9195b7fd0a
Require MFA to release rails 2021-11-15 20:37:42 +00:00
Jean Boussier
bf33510d86 Optimize CurrentAttributes method generation
The bulk of the optimization is to generate code rather than use
`define_method` with a closure.

```
Warming up --------------------------------------
            original   207.468k i/100ms
      code-generator   340.849k i/100ms
Calculating -------------------------------------
            original      2.127M (± 1.1%) i/s -     10.788M in   5.073860s
      code-generator      3.426M (± 0.9%) i/s -     17.383M in   5.073965s

Comparison:
      code-generator:  3426241.0 i/s
            original:  2126539.2 i/s - 1.61x  (± 0.00) slower
```

```ruby

require 'benchmark/ips'
require 'active_support/all'

class Original < ActiveSupport::CurrentAttributes
  attribute :foo
end

class CodeGen < ActiveSupport::CurrentAttributes
  class << self
    def attribute(*names)
      ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
        names.each do |name|
          owner.define_cached_method(name, namespace: :current_attributes) do |batch|
            batch <<
              "def #{name}" <<
              "attributes[:#{name}]" <<
              "end"
          end
          owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
            batch <<
              "def #{name}=(value)" <<
              "attributes[:#{name}] = value" <<
              "end"
          end
        end
      end

      ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
        names.each do |name|
          owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
            batch <<
              "def #{name}" <<
              "instance.#{name}" <<
              "end"
          end
          owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
            batch <<
              "def #{name}=(value)" <<
              "instance.#{name} = value" <<
              "end"
          end
        end
      end
    end
  end
  attribute :foo
end

Benchmark.ips do |x|
  x.report('original') { Original.foo }
  x.report('code-generator') { CodeGen.foo }
  x.compare!
end
```
2021-11-02 15:52:25 +01:00
Rafael Mendonça França
3b5db8e8c9
Merge pull request #43378 from Stellenticket/set_empty_secure_password
clear secure password cache if password is set to `nil`
2021-10-14 17:59:25 -04:00
Markus Doits
9bd186a0e8
clear secure password cache if password is set to nil
```rb
# before:
user.password = 'something'
user.password = nil

user.password # => 'something'

# now:
user.password = 'something'
user.password = nil

user.password # => nil
```
2021-10-10 11:54:35 +02:00
Daniel Colson
26351067c1
Replace more ableist language
Along the same lines as ccb3cb573b, this commit removes unnecessary
references to mental health.

As in that commit, I think many of these are more descriptive than what
we had before.

The commit changes only tests and documentation.
2021-10-07 11:47:28 -04:00
Muhammad Muhammad Ibrahim
d1d4a54c23 Prevent error when authenticating user with a blank password digest
Co-authored-by: Petrik de Heus <petrik@deheus.net>
2021-09-24 09:28:10 +02:00
Jesse van der Pluijm
4862c2faf6 Fix typo: integer numbers (not integral) 2021-09-18 10:21:42 +02:00
Rafael Mendonça França
d177551c30
Preparing for 7.0.0.alpha2 release 2021-09-15 18:22:51 -04:00
Rafael Mendonça França
9b7be48212
Preparing for 7.0.0.alpha1 release 2021-09-15 17:55:08 -04:00
Petrik
c477d95604 Introduce ActiveModel::API
Currently `ActiveModel::Model` is defined as the minimum API to talk
with Action Pack and Action View.
Its name suggests it can be included to create Active Record type
models, but for creating models it's probably too minimal. For example
it's very common to include ActiveModel::Attributes as well.

By moving `ActiveModel::Model`'s implementation to a new
`ActiveModel::API` we keep a definition of the minimum API to talk with
Action Pack and Action View.

For `ActiveModel::Model` we only need to include `ActiveModel::API`.
This will allow adding more funcationality to `ActiveModel::Model` while
keeping backwards compatibility.

Co-authored-by: Nathaniel Watts <1141717+thewatts@users.noreply.github.com>
2021-09-15 18:24:47 +02:00
Guillermo Iguaran
75ea828409 Don't add attribute_names to ActiveModel::Serialization public API 2021-08-28 11:56:21 -07:00
Cody Cutrer
1fbb3ddcd3 avoid calling attributes.keys in ActiveModel#serializable_hash
for ActiveRecord objects, it will force the entire attributes hash to
be constructed, which may include expensive deserialization.
ActiveModel already has a simple attribute_names method defined which
returns attributes.keys, exactly like the code we're replacing.
ActiveRecord overrides that method, and returns the names of the
attributes _without_ having to deserialize any values.

this change can save a lot of CPU if you have a serialized column
on a model that you often load from the DB because it's not worth
the effort to customize the SELECT on every relation, but
also rarely expose in any JSON serializations (i.e. have a default
except option for it).
2021-08-17 18:59:37 -06:00
Rafael Mendonça França
18707ab17f
Standardize nodoc comments 2021-07-29 21:18:07 +00:00
Ryuta Kamizono
0f001f00eb Fix to_json after changes_applied for ActiveModel::Dirty object
Follow up to #41677.

Mutation tracking variables are not only `@mutations_from_database` but
also `@mutations_before_last_save`.
2021-07-24 08:22:18 +09:00
Jonathan Hefner
baa070c3b4
Merge pull request #42839 from jonathanhefner/doc-each_validator
Remove nodoc from EachValidator [ci-skip]
2021-07-23 13:27:35 -05:00
Jean Boussier
8512118f43
Merge pull request #42832 from lulalala/slim-errors-inspect
Slimmer ActiveModel::Errors#inspect message
2021-07-23 09:05:08 +02:00
lulalala
1523838567 Slimmer ActiveModel::Errors#inspect
Only show @errors array and hide @base
2021-07-23 12:29:46 +08:00
Jonathan Hefner
79bb7d0a2b Remove nodoc from EachValidator [ci-skip]
The Active Record Validations guide recommends using `EachValidator`,
and has done so for many years.
2021-07-22 10:35:48 -05:00
Marcelo Lauxen
d1df4c100f
Fix dirty check for Float::NaN and BigDecimal::NaN
Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720) and can't be compared with `==`.
2021-07-22 09:49:16 -03:00
Ryuta Kamizono
e50b0e3ab3 Fixup CHANGELOGs [ci skip] 2021-07-21 10:08:08 +09:00
OKURA Masafumi
67adc94e00 Add :nodoc to ActiveModel::Errors
Three classes in `active_model/errors` look internal only.
2021-07-09 00:01:10 +09:00
OKURA Masafumi
f96929ae5f Add missing require to active_model/naming
Using `delegate` at L151 causes `NoMethodError`.
Adding `require` resolves this.
2021-07-08 23:02:09 +09:00
Zachary Scott
bead3221c7 ✂️ 2021-06-30 08:31:17 +09:00
Rafael França
637d386fbb
Merge pull request #41677 from anilmaurya/fix-41521
Fixes #41521, ActiveModel::Dirty fails on to_json
2021-06-24 14:10:29 -04:00
Anil Kumar Maurya
ab1a58281c Fixes #41521, ActiveModel::Dirty fails on to_json 2021-06-24 10:31:48 +00:00
Eddie Lebow
ec207a415a
Formatting markup in comment 2021-06-23 22:41:13 -04:00
Aditya Bhutani
32af6add67 [ci skip] Fixing all <x> based occurances to <x>-based 2021-06-16 19:15:12 +05:30
Chris Salzberg
caced27393 Add ActiveModel::AttributeSet#values_for_database 2021-06-08 16:39:23 +09:00
Benoit Daloze
191ee5eec9 Prefer (...) over ruby2_keywords for ActiveModel::Type.lookup
* (...) is already used in several places in Rails and looks nicer.
2021-05-25 16:06:15 +02:00
Benoit Daloze
5d86e32ae2 Fix delegation in ActiveModel::Type.lookup
* Without the change the new test fails like this:
  Failure:
  ActiveModel::TypeTest#test_registering_a_new_type [test/cases/type_test.rb:21]:
  Expected: #<struct args={}>
    Actual: #<struct args=nil>
* (*args, **kwargs)-delegation is not correct on Ruby 2.7 unless the
  target always accepts keyword arguments (not the case for `Struct.new(:args).new`).
  See https://eregon.me/blog/2021/02/13/correct-delegation-in-ruby-2-27-3.html
2021-05-25 16:01:17 +02:00
Benoit Daloze
8ccc3bfc2d Fix delegation in ActiveModel::Type::Registry
* Without the change the new test fails like this:
  Failure:
  ActiveModel::Type::RegistryTest#test_a_class_can_be_registered_for_a_symbol [test/cases/type/registry_test.rb:16]:
  Expected: [{}, {}]
    Actual: [nil, nil]
* (*args, **kwargs)-delegation is not correct on Ruby 2.7 unless the
  target always accepts keyword arguments (not the case for `Array.new`).
  See https://eregon.me/blog/2021/02/13/correct-delegation-in-ruby-2-27-3.html
2021-05-21 13:50:33 +02:00
Jean Boussier
1e56b1d115 Fix ruby-master test suite (Psych 4.0.0)
Ruby master ships with Psych 4.0.0 which makes `YAML.load`
defaults to safe mode (https://github.com/ruby/psych/pull/487).

However since these YAML files are trustworthy sources
we can parse them with `unsafe_load`.
2021-05-19 14:21:21 +02:00
John Bampton
6e85b6b86e Add spell checking with codespell as a GitHub Action
`codespell` works with a small custom dictionary and seems to find perhaps more spelling mistakes than `misspell` which really only fixes commonly misspelled English words.

Not all spell checkers can check all file types and most spell checkers can't find all the errors.

https://github.com/codespell-project/codespell
https://pypi.org/project/codespell/
2021-05-04 14:46:21 +10:00
Jean Boussier
ca5542fed3 Cache and reuse generated attribute methods 2021-05-03 09:27:59 +02:00
Ryuta Kamizono
95b45c826d Change send back to public_send
It was accidentally changed in #40095.
2021-04-26 03:23:51 +09:00
Abhay Nikam
0b0b22a46a Fixes a typo and wordsmithing in the exception message. Typo: nor -> or 2021-04-24 21:10:24 +05:30
Ashik Salman
b0241f300c Added more test coverage for comparison validator. 2021-04-24 19:36:35 +05:30
Ryuta Kamizono
aa7a8db604 Fix numericality validator :in with invalid args to raise ArgumentError 2021-04-24 14:35:35 +09:00
Ryuta Kamizono
f83dbe358e Fix error message on comparison validator 2021-04-24 14:26:08 +09:00
Ryuta Kamizono
a1afc7726b Convert \r\n to \n 2021-04-24 14:10:18 +09:00
Ryuta Kamizono
8a23c85f6e Fix "uninitialized constant ActiveModel::Validations::NumericalityValidator::Comparability (NameError)"
Somehow it isn't caused on CI, but it consistently causes on locally.

```
% bin/test -w
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:85: warning: method redefined; discarding old validates_each
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:85: warning: previous definition of validates_each was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:89: warning: already initialized constant ActiveModel::Validations::ClassMethods::VALID_OPTIONS_FOR_VALIDATE
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:89: warning: previous definition of VALID_OPTIONS_FOR_VALIDATE was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:152: warning: method redefined; discarding old validate
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:152: warning: previous definition of validate was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:192: warning: method redefined; discarding old validators
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:192: warning: previous definition of validators was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:234: warning: method redefined; discarding old clear_validators!
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:234: warning: previous definition of clear_validators! was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:254: warning: method redefined; discarding old validators_on
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:254: warning: previous definition of validators_on was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:270: warning: method redefined; discarding old attribute_method?
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:270: warning: previous definition of attribute_method? was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:275: warning: method redefined; discarding old inherited
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:275: warning: previous definition of inherited was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:283: warning: method redefined; discarding old initialize_dup
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:283: warning: previous definition of initialize_dup was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:301: warning: method redefined; discarding old errors
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:301: warning: previous definition of errors was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:373: warning: method redefined; discarding old invalid?
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:373: warning: previous definition of invalid? was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:382: warning: method redefined; discarding old validate!
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:382: warning: previous definition of validate! was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:405: warning: method redefined; discarding old run_validations!
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:405: warning: previous definition of run_validations! was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:410: warning: method redefined; discarding old raise_validation_error
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:410: warning: previous definition of raise_validation_error was here
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:426: warning: method redefined; discarding old model
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:428: warning: method redefined; discarding old initialize
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:428: warning: previous definition of initialize was here
Traceback (most recent call last):
	23: from bin/test:5:in `<main>'
	22: from bin/test:5:in `require_relative'
	21: from /Users/kamipo/src/github.com/rails/rails/tools/test.rb:18:in `<top (required)>'
	20: from /Users/kamipo/src/github.com/rails/rails/railties/lib/rails/test_unit/runner.rb:40:in `run'
	19: from /Users/kamipo/src/github.com/rails/rails/railties/lib/rails/test_unit/runner.rb:52:in `load_tests'
	18: from /Users/kamipo/src/github.com/rails/rails/railties/lib/rails/test_unit/runner.rb:52:in `each'
	17: from /Users/kamipo/src/github.com/rails/rails/railties/lib/rails/test_unit/runner.rb:52:in `block in load_tests'
	16: from /Users/kamipo/src/github.com/rails/rails/railties/lib/rails/test_unit/runner.rb:52:in `require'
	15: from /Users/kamipo/src/github.com/rails/rails/activemodel/test/cases/attributes_dirty_test.rb:5:in `<top (required)>'
	14: from /Users/kamipo/src/github.com/rails/rails/activemodel/test/cases/attributes_dirty_test.rb:6:in `<class:AttributesDirtyTest>'
	13: from /Users/kamipo/src/github.com/rails/rails/activemodel/test/cases/attributes_dirty_test.rb:7:in `<class:DirtyModel>'
	12: from /Users/kamipo/src/github.com/rails/rails/activemodel/test/cases/attributes_dirty_test.rb:7:in `require'
	11: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/model.rb:3:in `<top (required)>'
	10: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/model.rb:59:in `<module:ActiveModel>'
	 9: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/model.rb:62:in `<module:Model>'
	 8: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/model.rb:62:in `require'
	 7: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:436:in `<top (required)>'
	 6: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:436:in `each'
	 5: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:436:in `block in <top (required)>'
	 4: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations.rb:436:in `require'
	 3: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations/numericality.rb:5:in `<top (required)>'
	 2: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations/numericality.rb:6:in `<module:ActiveModel>'
	 1: from /Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations/numericality.rb:7:in `<module:Validations>'
/Users/kamipo/src/github.com/rails/rails/activemodel/lib/active_model/validations/numericality.rb:8:in `<class:NumericalityValidator>': uninitialized constant ActiveModel::Validations::NumericalityValidator::Comparability (NameError)
```
2021-04-24 13:46:45 +09:00
Matthew Draper
536a2c0011
Merge pull request #40095 from ChaelCodes/cc-comparablity-validator
Add ComparisonValidator to validate comparison of any objects
2021-04-24 13:20:57 +09:30
Ryuta Kamizono
bbbc861f71 Enable Performance/MapCompact cop
Follow up to #42053.
2021-04-23 16:33:02 +09:00
Jean Boussier
6a5fb7dbd4
Merge pull request #41911 from Shopify/simplify-proxy-call
Allow to pass the method signature when defining attribute methods
2021-04-12 22:36:32 +02:00
Rafael Mendonça França
4354e3ae49
Don't define methods using the method modifier in the same line as the method
Our style guide use block method modifiers, not inline method modifiers.
2021-04-12 18:49:54 +00:00
Jean Boussier
eece095765 Allow to pass the method signature when defining attribute methods
This saves some array allocations from avoiding `*args`, as well
as makes the Method object `arity` and `parameters` correct.

e.g. before this patch, ArgumentError would be confusing:

```ruby
>> model.name_was(1)
ArgumentError: wrong number of arguments (given 2, expected 1)
```
2021-04-11 17:03:25 +02:00
Jean Boussier
c8ea5b8615 Use alias_method rather than define_method to handle non compilable names 2021-04-11 16:10:45 +02:00
Jean Boussier
3f59640016 Stop checking if ruby2_keywords is defined 2021-04-11 13:42:02 +02:00
Étienne Barrié
81d0653f84 Simplify ActiveModel & ActiveRecord Type::Registry
ActiveRecord::Type::Registry doesn't need to inherit from
ActiveModel::Type::Registry, and it makes both classes more simple.

Co-authored-by: Adrianna Chang <adrianna.chang@shopify.com>
2021-04-07 09:59:55 -04:00
Jean Boussier
39341de433 Remove useless include_private parameter in define_proxy_call
Since Ruby 2.7 `self.some_private_method` works fine.
So now that Ruby 2.7 is the minimal supported version,
`define_proxy_call` can always prepend `self.`
2021-03-20 14:27:53 -04:00
Ryuta Kamizono
c435af140e Don't use type.cast(value) to emulate unchecked serialized value in unboundable?
I used `type.cast(value)` to emulate unchecked serialized value in
`unboundable?`, since `RangeError` was raised only for the integer type,
so the emulation works enough for the integer type.

But since #41516, Enum types are also not always serializable, so
`type.cast(value)` may also be called for the enum types.

I've delegated `type.cast(value)` to the subtype if an unknown label is
passed to work the emulation even on Enum types in 3b6461b. But it is
strange to delegate to the subtype for the emulation only if an unknown
label is passed.

Instead of using `type.cast(value)` for the emulation, extend
`serializable?` to get unchecked serialized value if the value is not
serializable.
2021-03-15 12:23:40 +09:00
Ryuta Kamizono
4607f13c88 Use serializable? instead of rescue ::RangeError 2021-03-10 21:21:22 +09:00
Ryuta Kamizono
6ee96a8f42 Avoid extra query attribute allocation in _insert_record/_update_record
Since a `BindParam` object always has an attribute object as the value
in the Active Record usage, so `_insert_record`/`_update_record` could
be passed attribute set instead of wrapping casted value by a query
attribute.
2021-03-01 16:24:31 +09:00
Artem Yegorov
89db42b5e1
Add uncountable? method to ActiveModel::Name 2021-02-18 09:55:29 +03:00
Jonathan Hefner
167f5c8065 Fix inline code markup [ci-skip]
RDoc Markup does not support backticks the way Markdown does to mark up
inline code.  Additionally, `<tt>` must be used to mark up inline code
that includes spaces or certain punctuation characters (e.g. quotes).
2021-02-14 11:20:35 -06:00
Ryuta Kamizono
56ea638290 A value acts like time object should respond to getutc and getlocal 2021-02-11 15:05:25 +09:00
Ricardo Díaz
93cbc30f34 Use Enumerator#all? and Enumerator#any? with classes instead of iterations
These methods have changed in Ruby 2.5 to be more akin to grep:

https://bugs.ruby-lang.org/issues/11286

Using classes seems to be faster (and a bit more expressive) than iterating over
the collection items:

```
Warming up --------------------------------------
    #all? with class   504.000  i/100ms
     #all? with proc   189.000  i/100ms
Calculating -------------------------------------
    #all? with class      4.960k (± 1.6%) i/s -     25.200k in   5.082049s
     #all? with proc      1.874k (± 2.8%) i/s -      9.450k in   5.047866s

Comparison:
    #all? with class:     4959.9 i/s
     #all? with proc:     1873.8 i/s - 2.65x  (± 0.00) slower
```

Benchmark script:

```ruby
require "minitest/autorun"
require "benchmark/ips"

class BugTest < Minitest::Test
  def test_enumerators_with_classes
    arr = (1..10000).to_a << nil

    assert_equal arr.all?(Integer), arr.all? { |v| v.is_a?(Integer) }

    Benchmark.ips do |x|
      x.report("#all? with class") do
        arr.all?(Integer)
      end

      x.report("#all? with proc") do
        arr.all? { |v| v.is_a?(Integer) }
      end

      x.compare!
    end
  end
end
```
2021-02-07 01:29:50 -05:00
Ryuta Kamizono
978308ac87 Fix markup in CHANGELOGs [ci skip] 2021-02-07 05:45:53 +09:00
Rafael Mendonça França
1b455e2e9d
Rails 6.2 is now Rails 7.0
We have big plans for the next version of Rails and that
require big versions.
2021-02-04 16:47:16 +00:00
Rafael Mendonça França
6487836af8
Rails 7 requires Ruby 2.7 and prefer Ruby 3+
The code cleanup is comming in later commits but this
already remove support to Ruby < 2.7.
2021-02-04 16:34:53 +00:00
Rafael França
da418dc250
Merge pull request #41217 from Vin0uz/active-model-errors-add-doc
[ci-skip] Adding options example in ActiveModel::Errors doc
2021-02-03 16:18:26 -05:00
Kevin Vinhas
da0e869bd8 Adding options example in ActiveModel::Errors doc
Place errors with variable example under the `type is a symbol` block
Closes #41124
2021-02-03 21:55:10 +01:00
Rafael Mendonça França
9ea15f1927
Improve performance of time type cast for ISO dates
Before this patch we were appeding a new date in front of
an already existing date what was making the fast path of the
type cast to not trigger.

The following benchmark was used to drive this implementation:

https://gist.github.com/rafaelfranca/0f71c00613f924fcbcdb14b95e6f7661

Closes #41316.
2021-02-03 00:10:15 +00:00
Rachael Wright-Munn
2ed77a9577 Replace all references to invalid/valid with assert_invalid_values/assert_valid_values. 2021-01-23 21:43:19 -05:00
Rachael Wright-Munn
9a08a2f09c Add ComparisonValidator to Rails to support validations between two comparable values.
We allow for compare validations in NumericalityValidator, but these
only work on numbers. There are various comparisons people may want
to validate, from dates to strings, to custom comparisons.

```
validates_comparison_of :end_date, greater_than: :start_date
```

Refactor NumericalityValidator to share module Comparison with ComparabilityValidator
* Move creating the option_value into a reusable module
* Separate COMPARE_CHECKS which support compare functions and accept values
* Move odd/even checks to NUMBER_CHECKS as they can only be run on numbers
2021-01-23 21:24:25 -05:00
Ryuta Kamizono
ddaceddeab
Merge pull request #41166 from kamipo/raise_unknown_type_error_on_definition_time
Raise unknown type error on the definition time
2021-01-20 18:24:40 +09:00
Rafael Mendonça França
077c66d5d6
Rename master to main in all code references 2021-01-19 20:46:33 +00:00
Ryuta Kamizono
26e1fe4938 Raise unknown type error on the definition time
If unknow type is given for attribute (`attribute :foo, :unknown`),
unknown type error isn't raised on the definition time but runtime.

It should be raised on the definition time.
2021-01-19 15:52:12 +09:00
Ryuta Kamizono
e889cc55b0 Make ActiveModel's attribute behavior follow ActiveRecord's 2021-01-18 07:54:36 +09:00
Ryuta Kamizono
63835772bb
Merge pull request #41019 from intrip/40902-fix-numericality-validator
Use round(scale) in  ActiveModel NumericalityValidator
2021-01-14 17:14:31 +09:00
Jacopo
6657e3428b Change Numericaly validator to use round
f72f743 introduces truncate(scale) in the Numericality validator.
This behaviour conflicts with AR decimal type conversion,
which uses round(scale) instead.

Changes the Numericality validator in order to use
round(scale) for consistency.
2021-01-13 22:37:01 +01:00
Ryuta Kamizono
4db95d8432 Restore the ability that update/destroy optimistic locking object without default
The ability has lost due to reverted #39321 in #41049.

We should allow updating with dirty locking value to work the documented
usage, but if casted value has no difference (i.e. regarded as no dirty),
identify the object by the original (uninitialized default) value.
2021-01-12 09:27:11 +09:00
Ryuta Kamizono
8c60a2169a Add round up assertions for decimal tests 2021-01-12 06:16:21 +09:00
Ryuta Kamizono
d24dc88dad
Revert "Fix update with dirty locking column to not match latest object accidentally" 2021-01-08 18:06:05 +09:00
Michal Papis
2486e887de
Add validate numericality in range 2021-01-05 22:56:58 +01:00
Ryuta Kamizono
2b0b5a75c0 Bump license years to 2021 [ci skip] 2021-01-01 12:21:20 +09:00
Rafael Mendonça França
4740a2a02c
Add changelog entry for #40961 2020-12-29 19:25:04 +00:00
Rafael França
67a1cb6c19
Merge pull request #40961 from luk4s/add_activemodel_naming_locale_arg
change ActiveModel::Name initialize arguments to hash and add locale
2020-12-29 14:20:00 -05:00
Lukáš Pokorný
d20caa6df1
add locale argument to ActiveModel::Name initialize 2020-12-29 10:39:35 +01:00
Ryuta Kamizono
6c4306be71 Move set_options_for_callback into Callbacks::ClassMethods
https://buildkite.com/rails/rails/builds/73736#864bc31e-384d-4e38-8165-0d3256ac3f3d/968-979
2020-12-29 16:20:13 +09:00
Rafael Mendonça França
b4cab6a854
Make sure the :if options of callbacks is not mutated
In some cases, the framework was mutating the :if option of callbacks.
Since #38323, those options are frozen, so the framework could raise
exception when trying to mutate those options if they were being resued
with method like `with_options`.
2020-12-29 03:56:54 +00:00
alpaca-tc
8034a439c7 Reuse the same ActiveModel::Type::Value 2020-12-23 18:09:50 +09:00
Rafael Mendonça França
b24ed15baa
Rename the method to match what it is doing 2020-12-09 17:10:22 +00:00
Ryuta Kamizono
720a60e68c Split the options checks from read_attribute_for_validation 2020-12-09 16:43:26 +09:00
Ryuta Kamizono
1dee4990cd allow_nil should work for casted value in NumericalityValidator
It is a regression for 4cc438a1df75e4c230f19cafe9258dbab969cd27.

`NumericalityValidator` basically takes the value before typecasting,
but `allow_nil` should work for the typecasted value for the
compatibility.

Fixes #40750.
2020-12-08 23:25:56 +09:00
Rafael Mendonça França
59f7f5889e
Start Rails 6.2 development 🎉 2020-12-03 01:35:29 +00:00
alpaca-tc
3d1cb9a1f3 Handle frozen conditions in validate
Co-authored-by: Rafael França <rafael@franca.dev>
2020-11-06 12:06:03 +09:00
alpaca-tc
e4e62fbdc6 ActiveModel::Model can be frozen again 2020-11-05 20:52:27 +09:00
Rafael Mendonça França
8389f9902c
Preparing for 6.1.0.rc1 release 2020-11-02 21:12:47 +00:00
Rafael Mendonça França
7eed607b12
Fix CHANGELOG [ci skip] 2020-11-02 18:16:45 +00:00
Rafael França
af91d9a1c5
Merge pull request #40434 from filipe-sabella/pass-in-base-in-validation-messages
Pass in base to Error.human_attribute_names
2020-11-02 13:15:53 -05:00
Rafael Mendonça França
e14e78bf44
Revert "Add test to make sure this method will not be removed again"
This reverts commit d93a5d385e5bc2392a1f47dc2885e353898b62e1.

Revert "Revert "Remove unused internal methods in ActiveModel::Attributes""

This reverts commit 2d7967204e7f7d5ba846b0a6ed51088c7a7db365.

Reason: read_attribute was added in 6.1 as a performance optimization
and it is not needed anymore and write_attribute only existed to make
possible to call something that is not `attribute=` with send. We don't
need those methods internally and since they were never part of the
public API we can remove them.
2020-10-30 01:54:16 +00:00
Rafael Mendonça França
d93a5d385e
Add test to make sure this method will not be removed again 2020-10-28 21:26:39 +00:00
Rafael Mendonça França
2d7967204e
Revert "Remove unused internal methods in ActiveModel::Attributes"
This reverts commit 13bd289b448fb0186b2e932f306704ce2efb2fb6.
2020-10-28 21:26:38 +00:00
Ryuta Kamizono
e47068c3f3 Use attribute_before_type_cast in the internal
`read_attribute_before_type_cast` has become bit slower since #40395.
2020-10-27 16:38:32 +09:00
Ryuta Kamizono
13bd289b44 Remove unused internal methods in ActiveModel::Attributes 2020-10-27 16:11:24 +09:00
Akira Matsuda
fbe7bcf1d6 These test model accessors are public methods 2020-10-27 12:13:32 +09:00
Akira Matsuda
98fda672a6 *_digest is defined as a public method 2020-10-27 12:13:32 +09:00
Ryuta Kamizono
92ff708476 Re-enable Layout/SpaceAroundOperators cop
We prefer space around operators, but `Layout/SpaceAroundOperators` cop
was temporarily disabled in #36943 since that cop changed to check
alignment strictly somehow.

In RuboCop 1.0.0, that is fixed by https://github.com/rubocop-hq/rubocop/pull/8906.

Related https://github.com/rails/rails/pull/38034#discussion_r359845661,
https://github.com/rails/rails/pull/39770#discussion_r448829561.
2020-10-23 16:12:15 +09:00
Filipe Sabella
ac677fb1e3 Update CHANGELOG 2020-10-22 16:50:33 -03:00
Filipe Sabella
bc7b730891 Pass in base to Error.human_attribute_names
There are validation cases in which the human_attribute_name depends on
other fields of the base class.

For instance, an Address model that depends on the selected country to
localize the attribute name to be shown in error messages. E.g. the
:address1 and :address2 attributes can be displayed as very different
strings depending on whether the address is in the US or in Japan.
2020-10-22 16:50:00 -03:00
Chris Griego
5fe517563e
Add missing ActiveSupport require in ActiveModel::Serialization
serializable_attributes is using index_with from active_support/core_ext/enumerable
2020-10-13 14:30:24 -05:00
Akira Matsuda
a65dd81dbb 👮 2020-10-02 14:00:30 +09:00
Akira Matsuda
82ca591b4a Naming methods are defined as public methods 2020-10-02 13:50:14 +09:00
Akira Matsuda
5321f94066 Model attribute methods are publicly callable 2020-10-02 13:48:33 +09:00
Akira Matsuda
674fd96d48 Model#attribute_method_matchers is a public method 2020-10-02 13:47:39 +09:00
Akira Matsuda
ce25559c3e Comparison operator methods on Integer are public methods 2020-10-02 13:41:42 +09:00
Akira Matsuda
02ad67841c Integer#odd? and even? are public methods 2020-10-02 13:39:00 +09:00
Akira Matsuda
9d2a2c775a Add missing test for LengthValidation giving the value via Symbol 2020-10-02 13:37:08 +09:00
Akira Matsuda
c7e68d1729 ==, >=, and <= are public methods 2020-10-02 13:18:09 +09:00
Akira Matsuda
6d45be5ba0 *_confirmation is defined as a public method 2020-10-02 13:14:15 +09:00
Akira Matsuda
8d014e1058 Enumerable#include? and cover? are both public methods 2020-10-02 13:12:55 +09:00
Akira Matsuda
f2a182bf7d Time.utc and Time.local are both public methods 2020-10-02 13:06:59 +09:00
Akira Matsuda
9caf8ddb93 *_ditest and *_digest= are defined as public methods 2020-10-02 13:06:55 +09:00
Trevor John
6fba3c3be0
Allow ActiveModel::Name fields to be overriden 2020-09-23 00:17:54 -04:00
Akira Matsuda
badcaf6763 AR::Base#read_attribute_for_validation is a public_method 2020-09-16 12:15:23 +09:00
lulalala
1fee2cbc50 Rename Error#detail method as details
Plural is more expected.
2020-09-11 17:03:59 +08:00
Eugene Kenny
7af59e16a2 Use transform_values in a few more places
It's faster on Ruby 2.7+ since it avoids rehashing the keys.
2020-09-08 01:34:41 +01:00
Abhay Nikam
714f8c8068 Documents other_than option also accepts the proc or a symbol for numericality validation [skip ci] 2020-08-24 21:27:45 +05:30
Eugene Kenny
0d0eb93b16 Add missing require for Enumerable#index_with
Followup to 0adcec49541aac069600202ed5f83c8ef6f2197e.
2020-08-23 09:17:53 +01:00
Matthew Draper
843898c57a
Merge pull request #22610 from KevinSjoberg/feature/array-member-inclusion
Validate inclusion of each object in an array
2020-08-03 00:09:35 +09:30
Vipul A M
1d6daaaa56
Merge pull request #39735 from lulalala/doc-errors-update
Document ActiveModel errors methods
2020-07-31 08:54:08 +05:30
Ryuta Kamizono
4cc438a1df Extract read_attribute_for_validation for per-validator customization
The model global `read_attribute_for_validation` is not fit for some
validators (specifically for numericality validator).

To allow per-validator customization for attribute value, it extracts
`read_attribute_for_validation` in `EachValidator`.
2020-07-29 18:11:49 +09:00
lulalala
f9518dc972 Document model error methods
[ci skip]
2020-06-29 17:50:08 +08:00
Ryuta Kamizono
f870537c47 Allow ISO 8601 formatted string for fast_string_to_time
Currently `ISO_DATETIME` regexp parser doesn't allow ISO 8601 formatted
string, it fallbacks to slower `Date._parse` in `fallback_string_to_time`.

This makes `ISO_DATETIME` allows ISO 8601 formatted string for
`fast_string_to_time`, it makes ISO 8601 formatted string parsing about
3.5x faster.

```ruby
type = ActiveRecord::Type.lookup(:datetime)
local = Time.now.iso8601
utc = Time.now.utc.iso8601

Benchmark.ips do |x|
  x.report("type.cast(local)") { type.cast(local); type.cast(local) }
  x.report("type.cast(utc)")   { type.cast(utc);   type.cast(utc)   }
end
```

Before:

```
Warming up --------------------------------------
    type.cast(local)     2.198k i/100ms
      type.cast(utc)     2.443k i/100ms
Calculating -------------------------------------
    type.cast(local)     22.371k (± 5.9%) i/s -    112.098k in   5.028419s
      type.cast(utc)     23.359k (± 5.5%) i/s -    117.264k in   5.034913s
```

After:

```
Warming up --------------------------------------
    type.cast(local)     6.918k i/100ms
      type.cast(utc)     9.414k i/100ms
Calculating -------------------------------------
    type.cast(local)     71.468k (± 6.2%) i/s -    359.736k in   5.053612s
      type.cast(utc)     86.258k (± 4.5%) i/s -    433.044k in   5.029979s
```
2020-06-20 10:50:08 +09:00
Ryuta Kamizono
575f4e16a7 Do not use slower public_send in type casting
Before:

```
Warming up --------------------------------------
type.cast(usec=0len)     9.439k i/100ms
type.cast(usec=6len)     9.282k i/100ms
type.cast(usec=3len)     9.111k i/100ms
type.cast(usec=7len)     9.059k i/100ms
Calculating -------------------------------------
type.cast(usec=0len)     90.341k (± 4.2%) i/s -    453.072k in   5.023724s
type.cast(usec=6len)     84.970k (± 3.7%) i/s -    426.972k in   5.031444s
type.cast(usec=3len)     84.043k (± 4.3%) i/s -    428.217k in   5.104155s
type.cast(usec=7len)     83.443k (± 5.3%) i/s -    416.714k in   5.008011s
```

After:

```
Warming up --------------------------------------
type.cast(usec=0len)     9.829k i/100ms
type.cast(usec=6len)     9.405k i/100ms
type.cast(usec=3len)     9.315k i/100ms
type.cast(usec=7len)     9.464k i/100ms
Calculating -------------------------------------
type.cast(usec=0len)     92.918k (± 5.3%) i/s -    471.792k in   5.091504s
type.cast(usec=6len)     86.207k (± 4.4%) i/s -    432.630k in   5.027844s
type.cast(usec=3len)     86.060k (± 4.6%) i/s -    437.805k in   5.097409s
type.cast(usec=7len)     87.036k (± 4.3%) i/s -    435.344k in   5.010501s
```
2020-06-20 08:22:45 +09:00
Ryuta Kamizono
e152d3403f Make usec parsing faster
This intends to avoid extra Rational creation for usec parsing
especially in the no usec case.

This makes all usec variations faster.

```ruby
type = ActiveRecord::Type.lookup(:datetime)
time1 = "2020-06-19 19:18:43"
time2 = "2020-06-19 19:18:43.123456"
time3 = "2020-06-19 19:18:43.123"
time4 = "2020-06-19 19:18:43.1234567"

Benchmark.ips do |x|
  x.report("type.cast(usec=0len)") { type.cast(time1); type.cast(time1) }
  x.report("type.cast(usec=6len)") { type.cast(time2); type.cast(time2) }
  x.report("type.cast(usec=3len)") { type.cast(time3); type.cast(time3) }
  x.report("type.cast(usec=7len)") { type.cast(time4); type.cast(time4) }
end
```

Before:

```
Warming up --------------------------------------
type.cast(usec=0len)     7.343k i/100ms
type.cast(usec=6len)     8.993k i/100ms
type.cast(usec=3len)     8.301k i/100ms
type.cast(usec=7len)     8.209k i/100ms
Calculating -------------------------------------
type.cast(usec=0len)     85.825k (± 5.4%) i/s -    433.237k in   5.062319s
type.cast(usec=6len)     82.045k (± 4.3%) i/s -    413.678k in   5.051260s
type.cast(usec=3len)     78.659k (± 4.4%) i/s -    398.448k in   5.074891s
type.cast(usec=7len)     77.477k (± 4.4%) i/s -    394.032k in   5.095355s
```

After:

```
Warming up --------------------------------------
type.cast(usec=0len)     9.439k i/100ms
type.cast(usec=6len)     9.282k i/100ms
type.cast(usec=3len)     9.111k i/100ms
type.cast(usec=7len)     9.059k i/100ms
Calculating -------------------------------------
type.cast(usec=0len)     90.341k (± 4.2%) i/s -    453.072k in   5.023724s
type.cast(usec=6len)     84.970k (± 3.7%) i/s -    426.972k in   5.031444s
type.cast(usec=3len)     84.043k (± 4.3%) i/s -    428.217k in   5.104155s
type.cast(usec=7len)     83.443k (± 5.3%) i/s -    416.714k in   5.008011s
```
2020-06-20 08:08:31 +09:00
Ryuta Kamizono
d2cdf0be67
Merge pull request #39612 from kamipo/faster_attributes
PERF: 45% faster attributes for readonly usage
2020-06-17 20:29:47 +09:00
Ryuta Kamizono
3944fb743a Avoid to use slower define_method for AcceptsMultiparameterTime
This makes `datetime.serialize` about 10% faster.

```ruby
type = ActiveRecord::Type.lookup(:datetime)
time = Time.now.utc

Benchmark.ips do |x|
  x.report("type.serialize(time)") do
    type.serialize(time)
    type.serialize(time)
    type.serialize(time)
    type.serialize(time)
  end
end
```

Before:

```
Warming up --------------------------------------
type.serialize(time)    12.899k i/100ms
Calculating -------------------------------------
type.serialize(time)    131.293k (± 1.6%) i/s -    657.849k in   5.011870s
```

After:

```
Warming up --------------------------------------
type.serialize(time)    14.603k i/100ms
Calculating -------------------------------------
type.serialize(time)    145.941k (± 1.1%) i/s -    730.150k in   5.003639s
```
2020-06-17 19:45:00 +09:00