1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
Commit graph

21 commits

Author SHA1 Message Date
Sean Griffin
9e25e0e173 Implement equality comparison on AttributeSet and friends
Any gems or libraries which do work with serialization or YAML will
ultimately need to compare these objects (albeit indirectly) to ensure
correctness. These will likely never get used internally (as they're
slow), but we should still expose them for others.
2015-10-06 08:52:28 -06:00
Sean Griffin
fb03a9ab35 Separate dup from deep_dup in the attributes hash
I'm looking to move towards a tree-like structure for dirty checking
that involves an attribute holding onto the attribute that it was
created from. This means that `changed?` can be fully encapsulated on
that object. Since the objects are immutable, in `changes_applied`, we
can simply perform a shallow dup, instead of a deep one.

I'm not sure if that will actually end up in a performance boost, but
I'd like to semantically separate these concepts regardless
2015-09-28 16:26:50 -04:00
Sean Griffin
8e633e5058 Clean up the implementation of AR::Dirty
This moves a bit more of the logic required for dirty checking into the
attribute objects. I had hoped to remove the `with_value_from_database`
stuff, but unfortunately just calling `dup` on the attribute objects
isn't enough, since the values might contain deeply nested data
structures. I think this can be cleaned up further.

This makes most dirty checking become lazy, and reduces the number of
object allocations and amount of CPU time when assigning a value. This
opens the door (but doesn't quite finish) to improving the performance
of writes to a place comparable to 4.1
2015-09-24 14:06:59 -06:00
Sean Griffin
9ca6948f72 type_cast_from_user -> cast 2015-02-17 13:39:42 -07:00
Sean Griffin
4a3cb840b0 Type#type_cast_from_database -> Type#deserialize 2015-02-17 13:28:48 -07:00
Sean Griffin
2f8c596d1b Maintain a consistent order in ActiveRecord::Base#attributes
Fixes #18871
2015-02-10 08:21:46 -07:00
Sean Griffin
be9b68038e Introduce ActiveRecord::Base#accessed_fields
This method can be used to see all of the fields on a model which have
been read. This can be useful during development mode to quickly find
out which fields need to be selected. For performance critical pages, if
you are not using all of the fields of a database, an easy performance
win is only selecting the fields which you need. By calling this method
at the end of a controller action, it's easy to determine which fields
need to be selected.

While writing this, I also noticed a place for an easy performance win
internally which I had been wanting to introduce. You cannot mutate a
field which you have not read. Therefore, we can skip the calculation of
in place changes if we have never read from the field. This can
significantly speed up methods like `#changed?` if any of the fields
have an expensive mutable type (like `serialize`)

```
Calculating -------------------------------------
 #changed? with serialized column (before)
                       391.000  i/100ms
 #changed? with serialized column (after)
                         1.514k i/100ms
-------------------------------------------------
 #changed? with serialized column (before)
                          4.243k (± 3.7%) i/s -     21.505k
 #changed? with serialized column (after)
                         16.789k (± 3.2%) i/s -     84.784k
```
2015-01-20 14:42:15 -07:00
Sean Griffin
7daeb98c76 Don't error when attributes is called on a frozen AR model
`freeze` will ultimately end up freezing the `AttributeSet`, which in
turn freezes its `@attributes` hash. However, we actually insert a
special object to lazily instantiate the values of the hash on demand.
When it does need to actually instantiate all of them for iteration (the
only case is `ActiveRecord::Base#attributes`, which calls
`AttributeSet#to_h`), it will set an instance variable as a performance
optimization

Since it's just an optimization for subsequent calls, and that method
being called at all is a very uncommon case, we can just leave the ivar
alone if we're frozen, as opposed to coming up with some overly
complicated mechanism for freezing which allows us to continue to modify
ourselves.

Fixes #17960
2014-12-08 12:48:24 -07:00
Sean Griffin
3f63ac4e4d Correctly determine if an attribute is uninitialized
In real usage, we give the builder a types hash with a default value of
`Type::Value.new`. This means we need to explicitly check for the key,
rather than the truthiness of the type to determine if it's a known but
uninitialized attribute
2014-11-14 14:30:40 -07:00
Sean Griffin
0f29c21607 Reduce the amount of work performed when instantiating AR models
We don't know which attributes will or won't be used, and we don't want
to create massive bottlenecks at instantiation. Rather than doing *any*
iteration over types and values, we can lazily instantiate the object.

The lazy attribute hash should not fully implement hash, or subclass
hash at any point in the future. It is not meant to be a replacement,
but instead implement its own interface which happens to overlap.
2014-11-14 14:30:40 -07:00
Sean Griffin
9c83e8401d AttributeSet#include? -> AttributeSet#key?
https://github.com/rails/rails/pull/15868/files#r14135210
2014-07-11 07:50:16 -06:00
Sean Griffin
bb7bc499e5 Attribute should know about its name
This allows using polymorphism for the uninitialized attributes raising
an exception behavior.
2014-06-26 07:18:21 -06:00
Sean Griffin
14b1208dd3 Encapsulate the creation of Attribute objects
This will make it less painful to add additional properties, which
should persist across writes, such as `name`.

Conflicts:
	activerecord/lib/active_record/attribute_set.rb
2014-06-26 06:45:57 -03:00
Rafael Mendonça França
2571c3f415 Merge pull request #15868 from sgrif/sg-uninitialized-attributes
Move behavior of `read_attribute` to `AttributeSet`

Conflicts:
	activerecord/lib/active_record/attribute_set.rb
	activerecord/test/cases/attribute_set_test.rb
2014-06-26 06:40:08 -03:00
Rafael Mendonça França
e1ad3ed773 Merge pull request #15846 from sgrif/sg-attributes-before-type-cast
Move `attributes_before_type_cast` to `AttributeSet`

Conflicts:
	activerecord/lib/active_record/attribute_set.rb
	activerecord/test/cases/attribute_set_test.rb
2014-06-26 06:26:26 -03:00
Sean Griffin
a89f8a922d Move behavior of read_attribute to AttributeSet
Moved `Builder` to its own file, as it started looking very weird once I
added private methods to the `AttributeSet` class and the `Builder`
class started to grow.

Would like to refactor `fetch_value` to change to

```ruby
self[name].value(&block)
```

But that requires the attributes to know about their name, which they
currently do not.
2014-06-25 08:51:15 -06:00
Yves Senn
5686fd0ca1 Merge pull request #15839 from sgrif/sg-attr-set-null
Return a null object from `AttributeSet#[]`
2014-06-22 13:59:27 +02:00
Sean Griffin
3e422e201d Move attributes to the AttributeSet object. 2014-06-21 13:09:48 -06:00
Sean Griffin
3ea9a88d80 Move attributes_before_type_cast to AttributeSet 2014-06-21 10:52:19 -06:00
Sean Griffin
6d7ac31ddb Return a null object from AttributeSet#[] 2014-06-20 11:36:23 -06:00
Sean Griffin
099af48d31 Introduce an object to aid in creation and management of @attributes
Mostly delegation to start, but we can start moving a lot of behavior in
bulk to this object.
2014-06-19 13:12:52 -06:00