Commit Graph

1477 Commits

Author SHA1 Message Date
bogdanvlviv
d1941fe6da
Fix active_model/errors docs [ci skip]
- Fix indentation.
- Add a missing dot to the end of the sentence.

Related to #32956
2018-06-12 00:32:25 +03:00
Rafael França
d3f659e526
Merge pull request #32956 from Shopify/i18n_activemodel_errors_full_message
Allow to override the full_message error format
2018-06-11 10:10:23 -04:00
Sam
a46dcb7454 PERF: avoid allocating column names where possible
When requesting columns names from database adapters AR:Result
would dup/freeze column names, this prefers using fstrings which
cuts down on repeat allocations

Attributes that are retained keep these fstrings around for the long
term

Note, this has the highest impact on "short" result sets, eg: Topic.first where you can void allocating the number of columns * String.
2018-06-06 09:50:58 +10:00
Martin Larochelle
32513c4b35 Add global config for config.active_model.i18n_full_message 2018-06-05 13:25:24 -04:00
Ryuta Kamizono
34cc301f03 Ensure casting by boolean attribute when querying
`QueryAttribute#value_for_database` calls only `type.serialize`, and
`Boolean#serialize` is a no-op unlike other attribute types.

It caused the issue #32624. Whether or not `serialize` will invoke
`cast` is undefined in our test cases, but it actually does not work
properly unless it does so for now.

Fixes #32624.
2018-05-29 05:22:31 +09:00
Ryuta Kamizono
fe9547b6fb Parse raw value only when a value came from user in numericality validator
Since `parse_raw_value_as_a_number` may not always parse raw value from
database as a number without type casting (e.g. "$150.55" as money
format).

Fixes #32531.
2018-05-28 10:12:15 +09:00
Ryuta Kamizono
8a60018355 Make force equality checking more strictly not to allow serialized attribute
Since #26074, introduced force equality checking to build a predicate
consistently for both `find` and `create` (fixes #27313).

But the assumption that only array/range attribute have subtype was
wrong. We need to make force equality checking more strictly not to
allow serialized attribute.

Fixes #32761.
2018-05-25 23:55:38 +09:00
Martin Larochelle
7d09874a71 Allow to override the full_message error format 2018-05-22 16:31:30 -04:00
Annie-Claude Côté
6ff593ef87 Fix user_input_in_time_zone to coerce non valid string into nil
Before it was coercing an invalid string into "2000-01-01 00:00:00".
2018-05-16 17:01:07 -04:00
Annie-Claude Côté
35bf8e90b1 Add missing require for string to timezone conversion
Inside user_input_in_time_zone we call in_time_zone on the value and value can be a String.
2018-05-16 15:48:12 -04:00
Cassidy Kobewka
8b13506217
Update validates_inclusion_of example 2018-04-17 21:16:33 -04:00
Cassidy Kobewka
3137a8739e
Use string-based fields. [ci skip] 2018-04-16 19:09:45 -04:00
Cassidy Kobewka
2aa8e0a56f
Inclusive Language in Documentation Examples [ci skip] 2018-04-15 12:34:30 -04:00
Ryuta Kamizono
eb3740dcb0
Merge pull request #32498 from eugeneius/mutation_tracker_merge_changes
Prevent changes_to_save from mutating attributes
2018-04-10 13:18:34 +09:00
Eugene Kenny
80a09caedc Prevent changes_to_save from mutating attributes
When an array of hashes is added to a `HashWithIndifferentAccess`, the
hashes are replaced with HWIAs by mutating the array in place.

If an attribute's value is an array of hashes, `changes_to_save` will
convert it to an array of HWIAs as a side-effect of adding it to the
changes hash.

Using `merge!` instead of `[]=` fixes the problem, as `merge!` copies
any array values in the provided hash instead of mutating them.
2018-04-08 23:06:48 +01:00
Eugene Kenny
b9e1c0c4d7 Avoid generating full changes hash on every save
`changed_attribute_names_to_save` is called in `keys_for_partial_write`,
which is called on every save when partial writes are enabled.

We can avoid generating the full changes hash by asking the mutation
tracker for just the names of the changed attributes. At minimum this
saves one array allocation per attribute, but will also avoid calling
`Attribute#original_value` which is expensive for serialized attributes.
2018-04-08 22:56:31 +01:00
Andrew White
3f95054f1c Normalize date component when writing to time columns
For legacy reasons Rails stores time columns on sqlite as full
timestamp strings. However because the date component wasn't being
normalized this meant that when they were read back they were being
prefixed with 2001-01-01 by ActiveModel::Type::Time. This had a
twofold result - first it meant that the fast code path wasn't being
used because the string was invalid and second it was corrupting the
second fractional component being read by the Date._parse code path.

Fix this by a combination of normalizing the timestamps on writing
and also changing Active Model to be more lenient when detecting
whether a string starts with a date component before creating the
dummy time value for parsing.
2018-03-11 18:30:07 +00:00
Andrew White
4d9126cfcc Apply time column precision on assignment
In #20317, datetime columns had their precision applied on assignment but
that behaviour wasn't applied to time columns - this commit fixes that.

Fixes #30301.
2018-03-11 18:19:20 +00:00
Sean Griffin
e126078a0e Don't call changes in changes_applied unless required
This is an alternate implementation of #31698. That PR makes assumptions
that I do not want in the code base. We can fix the performance
regression with a much simpler patch.
2018-03-06 15:11:23 -07:00
Sean Griffin
5fcbdcfb57 Revert "PERF: Recover changes_applied performance (#31698)"
This reverts commit a19e91f0fab13cca61acdb1f33e27be2323b9786.
2018-03-06 15:11:22 -07:00
Jeremy Daer
4b42c7e52a Ruby 2.4: take advantage of String#unpack1
https://bugs.ruby-lang.org/issues/12752
https://ruby-doc.org/core-2.4.0/String.html#method-i-unpack1
2018-03-01 22:42:51 -08:00
Ryuta Kamizono
42a16a4d65 Alias assign_attributes to attributes= for AttributeAssignment
There is no reason `attributes=` doesn't take `assign_attributes`.
2018-02-28 19:58:45 +09:00
Rafael França
ea70d20441
Merge pull request #31926 from composerinteralia/am-attributes
Add ActiveModel::Attributes#attributes
2018-02-28 00:17:43 -05:00
Ryuta Kamizono
0605f45ab3 Merge pull request #28270 from mmangino/dont_ignore_seralization_options
Don't accidentally lose includes in serialization
2018-02-27 23:42:15 +09:00
Ryuta Kamizono
3579876931 Active Model: Use private attr_reader
Follow up of 6d63b5e49a399fe246afcebad45c3c962de268fa.
2018-02-26 06:16:24 +09:00
Ryuta Kamizono
cfbde022ee PostgreSQL: Allow BC dates like datetime consistently
BC dates are supported by both date and datetime types.

https://www.postgresql.org/docs/current/static/datatype-datetime.html

Since #1097, new datetime allows year zero as 1 BC, but new date does
not. It should be allowed even in new date consistently.
2018-02-23 11:15:00 +09:00
Jeremy Daer
1e526788e6 Rails 6 requires Ruby 2.3+ 2018-02-17 10:03:37 -08:00
Daniel Colson
043ce35b18 Add ActiveModel::Attributes#attributes
This starts to fix #31832.
ActiveModel::Attributes includes ActiveModel::AttributeMethods,
which requires an `#attributes` method that returns a hash with string keys.
2018-02-07 18:11:14 -05:00
Ryuta Kamizono
8f2bb58ba2
PERF: Recover marshaling dump/load performance (#31827)
* PERF: Recover marshaling dump/load performance

This performance regression which is described in #30680 was caused by
f0ddf87 due to force materialized `LazyAttributeHash`.

Since 95b86e5, default proc has been removed in the class, so it is no
longer needed that force materialized.

Avoiding force materialized will recover marshaling dump/load
performance.

Benchmark:

https://gist.github.com/blimmer/1360ea51cd3147bae8aeb7c6d09bff17

Before:

```
it took 0.6248569069430232 seconds to unmarshal the objects

Total allocated: 38681544 bytes (530060 objects)

allocated memory by class
-----------------------------------
  12138848  Hash
  10542384  String
   7920000  ActiveModel::Attribute::Uninitialized
   5600000  ActiveModel::Attribute::FromDatabase
   1200000  Foo
    880000  ActiveModel::LazyAttributeHash
    400000  ActiveModel::AttributeSet
        80  Integer
        72  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
        40  ActiveModel::Type::String
        40  ActiveRecord::Type::DateTime
        40  Object
        40  Range

allocated objects by class
-----------------------------------
    250052  String
    110000  ActiveModel::Attribute::Uninitialized
     70001  Hash
     70000  ActiveModel::Attribute::FromDatabase
     10000  ActiveModel::AttributeSet
     10000  ActiveModel::LazyAttributeHash
     10000  Foo
         2  Integer
         1  ActiveModel::Type::String
         1  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
         1  ActiveRecord::Type::DateTime
         1  Object
         1  Range
```

After:

```
it took 0.1660824950085953 seconds to unmarshal the objects

Total allocated: 13883811 bytes (220090 objects)

allocated memory by class
-----------------------------------
   5743371  String
   4940008  Hash
   1200000  Foo
    880000  ActiveModel::LazyAttributeHash
    720000  Array
    400000  ActiveModel::AttributeSet
        80  ActiveModel::Attribute::FromDatabase
        80  Integer
        72  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
        40  ActiveModel::Type::String
        40  ActiveModel::Type::Value
        40  ActiveRecord::Type::DateTime
        40  Object
        40  Range

allocated objects by class
-----------------------------------
    130077  String
     50004  Hash
     10000  ActiveModel::AttributeSet
     10000  ActiveModel::LazyAttributeHash
     10000  Array
     10000  Foo
         2  Integer
         1  ActiveModel::Attribute::FromDatabase
         1  ActiveModel::Type::String
         1  ActiveModel::Type::Value
         1  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
         1  ActiveRecord::Type::DateTime
         1  Object
         1  Range
```

Fixes #30680.

* Keep the `@delegate_hash` to avoid to lose any mutations that have been made to the record
2018-02-02 07:52:33 +09:00
Rafael Mendonça França
1c383df324 Start Rails 6.0 development!!!
🎉🎉🎉
2018-01-30 18:51:17 -05:00
Daniel Colson
0d50cae996 Use respond_to test helpers 2018-01-25 23:32:58 -05:00
Sean Griffin
0af36c62a5 Allow attributes with a proc default to be marshalled
We don't implement much custom marshalling logic for these objects, but
the proc default case needs to be handled separately. Unfortunately
there's no way to just say "do what you would have done but with this
value for one ivar", so we have to manually implement `marshal_load` as
well.

The test case is a little bit funky, but I'd really like an equality
test in there, and there's no easy way to add one now that this is out
of AR (since the `attributes` method isn't here)

Fixes #31216
2018-01-23 14:12:13 -07:00
Daniel Colson
00de46acf5 Use singular define_attribute_method
`define_attribute_methods` splats the arguments,
then calls out to `define_attribute_method` for
each. When defining a singule attribute, using
the singular version of the method saves us an
array and an extra method call.
2018-01-21 22:06:59 -05:00
Ryuta Kamizono
a19e91f0fa
PERF: Recover changes_applied performance (#31698)
#30985 caused `object.save` performance regression since calling
`changes` in `changes_applied` is very slow.
We don't need to call the expensive method in `changes_applied` as long
as `@attributes` is tracked by mutation tracker.

https://gist.github.com/kamipo/1a9f4f3891803b914fc72ede98268aa2

Before:

```
Warming up --------------------------------------
create_string_columns
                        73.000  i/100ms
Calculating -------------------------------------
create_string_columns
                        722.256  (± 5.8%) i/s -      3.650k in   5.073031s
```

After:

```
Warming up --------------------------------------
create_string_columns
                        96.000  i/100ms
Calculating -------------------------------------
create_string_columns
                        950.224  (± 7.7%) i/s -      4.800k in   5.084837s
```
2018-01-22 10:46:36 +09:00
Ryuta Kamizono
1a4eb55a82 deep_dup is used in AttributeSet#deep_dup 2018-01-13 13:09:31 +09:00
Ryuta Kamizono
a954e1e817 Merge pull request #29018 from willbryant/missing_attributes_after_save
fix the dirty tracking code's save hook overwriting missing attribute…
2018-01-03 05:14:18 +09:00
Ryuta Kamizono
8af6f1ccd4 Refactor to Array(options[:on]) only once in defining validations 2018-01-01 06:18:39 +09:00
Yoshiyuki Hirano
b20354afcc Bump license years for 2018 2017-12-31 22:36:55 +09:00
Yoshiyuki Hirano
470d0e459f Fix validation callbacks on multiple context
I found a bug that validation callbacks don't fire on multiple context.
So I've fixed it.

Example:

```ruby
class Dog
  include ActiveModel::Validations
  include ActiveModel::Validations::Callbacks

  attr_accessor :history

  def initialize
    @history = []
  end

  before_validation :set_before_validation_on_a, on: :a
  before_validation :set_before_validation_on_b, on: :b
  after_validation :set_after_validation_on_a, on: :a
  after_validation :set_after_validation_on_b, on: :b

  def set_before_validation_on_a; history << "before_validation on a"; end
  def set_before_validation_on_b; history << "before_validation on b"; end
  def set_after_validation_on_a;  history << "after_validation on a" ; end
  def set_after_validation_on_b;  history << "after_validation on b" ; end
end
```

Before:

```
d = Dog.new
d.valid?([:a, :b])
d.history # []
```

After:

```
d = Dog.new
d.valid?([:a, :b])
d.history # ["before_validation on a", "before_validation on b", "after_validation on a", "after_validation on b"]
```
2017-12-20 00:27:19 +09:00
Tom Copeland
683c8fd277 Fix doc typo [ci skip] 2017-12-12 16:34:19 -05:00
Lonre Wang
a6031e40ba
Update validates.rb 2017-12-10 22:17:48 +07:00
Rafael Mendonça França
2837d0f334
Preparing for 5.2.0.beta2 release 2017-11-28 14:41:02 -05:00
Sean Griffin
95b86e57a6 Change how AttributeSet::Builder receives its defaults
There are two concerns which are both being combined into one here, but
both have the same goal. There are certain attributes which we want to
always consider initialized. Previously, they were handled separately.
The primary key (which is assumed to be backed by a database column)
needs to be initialized, because there is a ton of code in Active Record
that assumes `foo.id` will never raise. Additionally, we want attributes
which aren't backed by a database column to always be initialized, since
we would never receive a database value for them.

Ultimately these two concerns can be combined into one. The old
implementation hid a lot of inherent complexity, and is hard to optimize
from the outside. We can simplify things significantly by just passing
in a hash.

This has slightly different semantics from the old behavior, in that
`Foo.select(:bar).first.id` will return the default value for the
primary key, rather than `nil` unconditionally -- however, the default
value is always `nil` in practice.
2017-11-27 14:06:51 -07:00
Rafael Mendonça França
cceeeb6e57
Preparing for 5.2.0.beta1 release 2017-11-27 14:50:03 -05:00
Rafael França
233d6a2b56
Merge pull request #31117 from renuo/fix_errors_added
fix bug on added? method
2017-11-13 16:13:54 -05:00
Alessandro Rodi
15cb4efadb fix bug on added? method
fix rubocop issues
2017-11-13 17:27:47 +01:00
Ryuta Kamizono
24b59434e6
Add missing autoload Type (#31123)
Attribute modules (`Attribute`, `Attributes`, `AttributeSet`) uses
`Type`, but referencing `Type` before the modules still fail.

```
% ./bin/test -w test/cases/attribute_test.rb -n test_with_value_from_user_validates_the_value
Run options: -n test_with_value_from_user_validates_the_value --seed 31876

E

Error:
ActiveModel::AttributeTest#test_with_value_from_user_validates_the_value:
NameError: uninitialized constant ActiveModel::AttributeTest::Type
    /Users/kamipo/src/github.com/rails/rails/activemodel/test/cases/attribute_test.rb:233:in `block in <class:AttributeTest>'

bin/test test/cases/attribute_test.rb:232

Finished in 0.002985s, 335.0479 runs/s, 335.0479 assertions/s.
1 runs, 1 assertions, 0 failures, 1 errors, 0 skips
```

Probably we need more autoloading at least `Type`.
2017-11-11 06:43:54 +09:00
Ryuta Kamizono
fc7a6c7381 Add missing require "active_support/core_ext/hash/indifferent_access"
https://travis-ci.org/rails/rails/jobs/300163454#L2236
2017-11-10 23:48:53 +09:00
yuuji.yaginuma
1cddc91a02 Add missing requires
Currently, executing the test with only `attribute_test.rb` results in an error.

```
./bin/test -w test/cases/attribute_test.rb
Run options: --seed 41205

# Running:

....E

Error:
ActiveModel::AttributeTest#test_attributes_do_not_equal_attributes_with_different_types:
NameError: uninitialized constant ActiveModel::AttributeTest::Type
    rails/activemodel/test/cases/attribute_test.rb:159:in `block in <class:AttributeTest>'

bin/test test/cases/attribute_test.rb:158
```

Added a missing require to fix this.
2017-11-10 19:26:32 +09:00
Ryuta Kamizono
2da92b7a94
Merge pull request #31114 from y-yagi/fix_ruby_warnings_in_active_model
Fix ruby warnings in Active Model
2017-11-10 17:48:23 +09:00