aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/attribute_set.rb
Commit message (Collapse)AuthorAgeFilesLines
* Implement equality comparison on `AttributeSet` and friendsSean Griffin2015-10-061-0/+4
| | | | | | | 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.
* Separate `dup` from `deep_dup` in the attributes hashSean Griffin2015-09-281-1/+7
| | | | | | | | | | | 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
* Clean up the implementation of AR::DirtySean Griffin2015-09-241-0/+5
| | | | | | | | | | | | | 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
* Significantly improve the performance of `_read_attribute` on JRubySean Griffin2015-02-061-2/+10
| | | | | | | | | | | | | The `&block` form is more than twice as fast as the manual form of delegation (and is the code I'd rather write anyway). Unfortunately, it's still twice as slow on MRI. However, this is enough of a hotspot to justify giving JRuby special treatment. I can't currently provide benchmarks in the context of Active Record, since the JDBC adapters still aren't updated for 4.2, but the actual work performed (assuming it's been read at least once already) will have nearly the same performance characteristics as https://gist.github.com/sgrif/b86832786551aaee74de.
* Attribute assignment and type casting has nothing to do with columnsSean Griffin2015-01-311-1/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | It's finally finished!!!!!!! The reason the Attributes API was kept private in 4.2 was due to some publicly visible implementation details. It was previously implemented by overloading `columns` and `columns_hash`, to make them return column objects which were modified with the attribute information. This meant that those methods LIED! We didn't change the database schema. We changed the attribute information on the class. That is wrong! It should be the other way around, where schema loading just calls the attributes API for you. And now it does! Yes, this means that there is nothing that happens in automatic schema loading that you couldn't manually do yourself. (There's still some funky cases where we hit the connection adapter that I need to handle, before we can turn off automatic schema detection entirely.) There were a few weird test failures caused by this that had to be fixed. The main source came from the fact that the attribute methods are now defined in terms of `attribute_names`, which has a clause like `return [] unless table_exists?`. I don't *think* this is an issue, since the only place this caused failures were in a fake adapter which didn't override `table_exists?`. Additionally, there were a few cases where tests were failing because a migration was run, but the model was not reloaded. I'm not sure why these started failing from this change, I might need to clear an additional cache in `reload_schema_from_cache`. Again, since this is not normal usage, and it's expected that `reset_column_information` will be called after the table is modified, I don't think it's a problem. Still, test failures that were unrelated to the change are worrying, and I need to dig into them further. Finally, I spent a lot of time debugging issues with the mutex used in `define_attribute_methods`. I think we can just remove that method entirely, and define the attribute methods *manually* in the call to `define_attribute`, which would simplify the code *tremendously*. Ok. now to make this damn thing public, and work on moving it up to Active Model.
* Remove `AttributeSet#initialized_keys`Sean Griffin2015-01-311-1/+1
| | | | | | | | | This method doesn't need to be lazy, as it is never called from reads. The only time it is called are in write cases, where we're about to loop through the results of it, and build the attribute objects anyway. So we don't gain anything by dodging the instantiation here. This is the only method that coupled `AttributeSet` to `LazyAttributeHash`, so removing it puts us back in a place where we can use a normal hash instead.
* Introduce `ActiveRecord::Base#accessed_fields`Sean Griffin2015-01-201-0/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 ```
* `update_column` take ruby-land input, not database-land inputSean Griffin2014-12-161-0/+4
| | | | | | | | | | | | | | | In the case of serialized columns, we would expect the unserialized value as input, not the serialized value. The original issue which made this distinction, #14163, introduced a bug. If you passed serialized input to the method, it would double serialize when it was sent to the database. You would see the wrong input upon reloading, or get an error if you had a specific type on the serialized column. To put it another way, `update_column` is a special case of `update_all`, which would take `['a']` and not `['a'].to_yaml`, but you would not pass data from `params` to it. Fixes #18037
* Improve the performance of reading attributesSean Griffin2014-11-181-2/+2
| | | | | | | We added a comparison to "id", and call to `self.class.primary_key` a *lot*. We also have performance hits from `&block` all over the place. We skip the check in a new method, in order to avoid breaking the behavior of `read_attribute`
* Reduce the amount of work performed when instantiating AR modelsSean Griffin2014-11-141-9/+5
| | | | | | | | | | 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.
* AttributeSet#include? -> AttributeSet#key?Sean Griffin2014-07-111-3/+3
| | | | https://github.com/rails/rails/pull/15868/files#r14135210
* Merge pull request #16015 from sgrif/sg-ensure-initializedRafael Mendonça França2014-07-021-0/+6
|\ | | | | | | | | | | | | Move pk initialization logic onto `AttributeSet` Conflicts: activerecord/lib/active_record/attribute_set.rb
| * Move pk initialization logic onto `AttributeSet`Sean Griffin2014-07-021-0/+6
| | | | | | | | Better encapsulates its internals from `ActiveRecord::Base`.
* | Don't error when `dup`ing a record with no PKSean Griffin2014-07-021-0/+6
|/
* Use `Hash#transform_values` to clean up `AttributeSet`Sean Griffin2014-06-291-7/+3
|
* `Attribute` should know about its nameSean Griffin2014-06-261-8/+6
| | | | | This allows using polymorphism for the uninitialized attributes raising an exception behavior.
* Encapsulate the creation of `Attribute` objectsSean Griffin2014-06-261-1/+9
| | | | | | | | 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
* Merge pull request #15868 from sgrif/sg-uninitialized-attributesRafael Mendonça França2014-06-261-17/+24
|\ | | | | | | | | | | | | | | Move behavior of `read_attribute` to `AttributeSet` Conflicts: activerecord/lib/active_record/attribute_set.rb activerecord/test/cases/attribute_set_test.rb
| * Move behavior of `read_attribute` to `AttributeSet`Sean Griffin2014-06-251-17/+24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* | Merge pull request #15846 from sgrif/sg-attributes-before-type-castRafael Mendonça França2014-06-261-1/+5
|\ \ | |/ |/| | | | | | | | | | | Move `attributes_before_type_cast` to `AttributeSet` Conflicts: activerecord/lib/active_record/attribute_set.rb activerecord/test/cases/attribute_set_test.rb
| * Move `attributes_before_type_cast` to `AttributeSet`Sean Griffin2014-06-211-1/+5
| |
* | add missing `:nodoc:` for recent refactorings. [ci skip]Yves Senn2014-06-241-2/+1
| | | | | | | | | | | | | | | | | | | | Adding `# :nodoc:` to the parent `class` / `module` is not going to ignore nested classes or modules. There is a modifier `# :nodoc: all` but sadly the containing class or module will continue to be in the docs. /cc @sgrif
* | `reload` should fully reload attributesSean Griffin2014-06-221-4/+0
| | | | | | | | | | | | `reload` is meant to put a record in the same state it would be if you were to do `Post.find(post.id)`. This means we should fully replace the attributes hash.
* | Merge pull request #15839 from sgrif/sg-attr-set-nullYves Senn2014-06-221-1/+2
|\ \ | | | | | | Return a null object from `AttributeSet#[]`
| * | Return a null object from `AttributeSet#[]`Sean Griffin2014-06-201-1/+2
| |/
* / Move `attributes` to the `AttributeSet` object.Sean Griffin2014-06-211-0/+5
|/
* Introduce an object to aid in creation and management of `@attributes`Sean Griffin2014-06-191-0/+51
Mostly delegation to start, but we can start moving a lot of behavior in bulk to this object.