| Commit message (Collapse) | Author | Age | Files | Lines |
|\
| |
| | |
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.
|
|\ \
| | |
| | |
| | | |
Don't accidentally lose includes in serialization
|
| | | |
|
| | |
| | |
| | |
| | | |
Follow up of 6d63b5e49a399fe246afcebad45c3c962de268fa.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|
| |/
|/| |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
* 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
|
| |
| |
| |
| | |
:tada::tada::tada:
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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
|
| |
| |
| |
| |
| |
| |
| |
| | |
`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.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
#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
```
|
| | |
|
|\ \
| | |
| | |
| | | |
fix the dirty tracking code's save hook overwriting missing attribute…
|
| | | |
|
| | | |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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"]
```
|
| | | |
|
| | | |
|
| | | |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|
| | | |
|
|\ \ \
| | | |
| | | | |
fix bug on added? method
|
| | | |
| | | |
| | | |
| | | | |
fix rubocop issues
|
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | | |
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`.
|
| | | |
| | | |
| | | |
| | | | |
https://travis-ci.org/rails/rails/jobs/300163454#L2236
|
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | | |
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.
|
|\ \ \ \
| | | | |
| | | | | |
Fix ruby warnings in Active Model
|
| | | | | |
|
| |/ / / |
|
|/ / /
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Currently, executing the test with only `attribute_set_test.rb` results in an error.
```
./bin/test -w test/cases/attribute_set_test.rb
Run options: --seed 33470
# Running:
E
Error:
ActiveModel::AttributeSetTest#test_#map_returns_a_new_attribute_set_with_the_changes_applied:
NameError: uninitialized constant ActiveModel::AttributeSetTest::AttributeSet
Did you mean? ActiveModel::Attributes
ActiveModel::Attribute
activemodel/test/cases/attribute_set_test.rb:235:in `block in <class:AttributeSetTest>'
bin/test test/cases/attribute_set_test.rb:234
```
Added a missing require to fix this.
Also, I suspect that this is the cause of failures in CI.
Ref: https://travis-ci.org/rails/rails/jobs/299994708
|
| | |
| | |
| | |
| | |
| | | |
Use these to back the attributes API. Stop automatically including
ActiveModel::Dirty in ActiveModel::Attributes, and make it optional.
|
| | |
| | |
| | |
| | | |
`false`
|
| | |
| | |
| | |
| | |
| | | |
This brings the Length validator in line with the Numericality
validator, which currently supports Proc & Symbol arguments
|
|\ \ \
| | | |
| | | | |
Start bringing attributes API to AM
|
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | | |
This is the first PR of a WIP to bring the attributes API to
ActiveModel. It is not yet ready for public API.
The `attributes_dirty_test.rb` file was created based on `dirty_test.rb`,
and the simplifications in the diff do much to motivate this change.
```
diff activemodel/test/cases/dirty_test.rb activemodel/test/cases/attributes_dirty_test.rb
3a4
> require "active_model/attributes"
5c6
< class DirtyTest < ActiveModel::TestCase
---
> class AttributesDirtyTest < ActiveModel::TestCase
7,41c8,12
< include ActiveModel::Dirty
< define_attribute_methods :name, :color, :size
<
< def initialize
< @name = nil
< @color = nil
< @size = nil
< end
<
< def name
< @name
< end
<
< def name=(val)
< name_will_change!
< @name = val
< end
<
< def color
< @color
< end
<
< def color=(val)
< color_will_change! unless val == @color
< @color = val
< end
<
< def size
< @size
< end
<
< def size=(val)
< attribute_will_change!(:size) unless val == @size
< @size = val
< end
---
> include ActiveModel::Model
> include ActiveModel::Attributes
> attribute :name, :string
> attribute :color, :string
> attribute :size, :integer
```
|
|/ / /
| | |
| | |
| | | |
This basically reverts ee5cfc01a5797f854c8441539b0cae326a81b963
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Don't use remove_method or remove_possible_method just before a new
definition: at best the purpose is unclear, and at worst it creates a
race condition.
Instead, prefer redefine_method when practical, and
silence_redefinition_of_method otherwise.
|
| | | |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Besides making the code easier to read, this commit
also makes it faster:
* We don't eval `@base.class.respond_to?(:i18n_scope)` twice
* We only eval `@base.class.i18n_scope` once
* We don't call `flatten!` because it's not needed anymore
* We don't call `compact` because all elements are Symbols
|
| | | |
|
| | |
| | |
| | |
| | |
| | |
| | | |
[ci skip]
Closes #30012
|
|\ \ \
| | | |
| | | | |
Remove unused `Mutex_m` in Active Model
|
| | | |
| | | |
| | | |
| | | | |
Because `generated_attribute_methods` is an internal API.
|
| | | | |
|
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | | |
Between 4.2 and 5.0 the behavior of how multiparameter attributes
interact with `_before_type_cast` changed. In 4.2 it returns the
post-type-cast value. After 5.0, it returns the hash that gets sent to
the type. This behavior is correct, but will cause an issue if you then
tried to render that value in an input like `text_field` or
`hidden_field`.
In this case, we want those fields to use the post-type-cast form,
instead of the `_before_type_cast` (the main reason it uses
`_before_type_cast` at all is to avoid losing data when casting a
non-numeric string to integer).
I've opted to modify `came_from_user?` rather than introduce a new
method for this as I want to avoid complicating that contract further,
and technically the multiparameter hash didn't come from assignment, it
was constructed internally by AR.
Close #27888.
|
|/ / / |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
ActiveModel::Errors#merge! allows ActiveModel::Errors to append errors from
a separate ActiveModel::Errors instance onto their own.
Example:
person = Person.new
person.errors.add(:name, :blank)
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
person.errors.merge!(errors)
puts person.errors.messages
# => { name: ["can't be blank", "is invalid"] }
|
| | |
| | |
| | |
| | |
| | | |
`ActiveModel::Type::DateTime#serialize` should return a `Time` object
so that finding by a datetime column works correctly.
|