-
Couldn't load subscription status.
- Fork 528
Associations: #decorate queries DB every time
#932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Associations: #decorate queries DB every time
#932
Conversation
ee77d42 to
00e99b6
Compare
|
I'm sorry, but the test environment looks like a mess for me 😕 I spent some time figuring out how it's supposed to test Active Record integration — and found nothing looking like a valid example. Was it ever tested? 🤔 So, I've tested it manually. Could we pull this in without a test coverage for now? |
00e99b6 to
ba324ee
Compare
I'm sorry I've misunderstood something, but I think we can add these kinds of specs under the diff --git a/spec/dummy/app/decorators/comment_decorator.rb b/spec/dummy/app/decorators/comment_decorator.rb
new file mode 100644
index 0000000..8257739
--- /dev/null
+++ b/spec/dummy/app/decorators/comment_decorator.rb
@@ -0,0 +1,2 @@
+class CommentDecorator < Draper::Decorator
+end
\ No newline at end of file
diff --git a/spec/dummy/app/models/comment.rb b/spec/dummy/app/models/comment.rb
new file mode 100644
index 0000000..8b86c56
--- /dev/null
+++ b/spec/dummy/app/models/comment.rb
@@ -0,0 +1,3 @@
+class Comment < ApplicationRecord
+ belongs_to :post
+end
diff --git a/spec/dummy/app/models/post.rb b/spec/dummy/app/models/post.rb
index 6352fdb..d143128 100644
--- a/spec/dummy/app/models/post.rb
+++ b/spec/dummy/app/models/post.rb
@@ -4,4 +4,6 @@ class Post < ApplicationRecord
# attr_accessible :title, :body
broadcasts if defined? Turbo::Broadcastable
+
+ has_many :comments
end
diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb
index 9aebf2c..8213494 100644
--- a/spec/dummy/db/schema.rb
+++ b/spec/dummy/db/schema.rb
@@ -18,4 +18,9 @@ ActiveRecord::Schema.define(version: 20121019115657) do
t.datetime "updated_at", null: false
end
+ create_table "comments", force: true do |t|
+ t.integer "post_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
end
diff --git a/spec/dummy/spec/models/post_spec.rb b/spec/dummy/spec/models/post_spec.rb
index 8bc05a0..c8dc10e 100644
--- a/spec/dummy/spec/models/post_spec.rb
+++ b/spec/dummy/spec/models/post_spec.rb
@@ -18,4 +18,13 @@ RSpec.describe Post do
}
end
end if defined? Turbo::Broadcastable
+
+ describe 'association' do
+ it 'respects unsaved records' do
+ post = Post.create!
+ Comment.create!(post: post)
+ post.comments.build
+ expect(post.comments.decorate.count).to eq 2
+ end
+ end
end |
a9c1da1 to
f621594
Compare
f621594 to
4fc079c
Compare
`ActiveRecord::Associations::CollectionProxy#decorate` ignores `target` (be it already loaded or not) and loads the association again every time it's called. That's why unsaved records get missing from the decorated collection. Resolves drapergem#646, drapergem#706, drapergem#827.
4fc079c to
47ccabc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Similar to drapergem#932 for associations, calling `#decorate` on a relation loads the records every time, regardless of whether the underlying relation has already been loaded. This is because the default of passing `all` as the collection to decorate ends up being a new relation, so even if it were previously loaded, the new relation including `all` needs to be loaded. `ActiveRecord::Associations::CollectionProxy` actually inherits from `ActiveRecord::Relation`, so by pushing this up to the relation, we can cover both scenarios. https://github.com/rails/rails/blob/57c24948eb5cc9e5f9a4cecb6f2060f53e2246e1/activerecord/lib/active_record/associations/collection_proxy.rb#L31 The documentation included in drapergem#932 (`company.products.popular.decorate`) wasn't correct as you're decorating a relation there (`popular`), not just the association (`products`). This now covers the association itself (`company.products.decorate`), as well as a relation from that association (`company.products.popular.decorate`), as well as a relation right from the class without associations (`Product.popular.decorate`). Decorating the class itself (`Product.decorate`) still calls `all` since the model class is not a relation itself.
Similar to drapergem#932 for associations, calling `#decorate` on a relation loads the records every time, regardless of whether the underlying relation has already been loaded. This is because the default of passing `all` as the collection to decorate ends up being a new relation, so even if it were previously loaded, the new relation including `all` needs to be loaded. `ActiveRecord::Associations::CollectionProxy` actually inherits from `ActiveRecord::Relation`, so by pushing this up to the relation, we can cover both scenarios. https://github.com/rails/rails/blob/57c24948eb5cc9e5f9a4cecb6f2060f53e2246e1/activerecord/lib/active_record/associations/collection_proxy.rb#L31 The documentation included in drapergem#932 (`company.products.popular.decorate`) wasn't correct as you're decorating a relation there (`popular`), not just the association (`products`). This now covers the association itself (`company.products.decorate`), as well as a relation from that association (`company.products.popular.decorate`), as well as a relation right from the class without associations (`Product.popular.decorate`). Decorating the class itself (`Product.decorate`) still calls `all` since the model class is not a relation itself.
Similar to drapergem#932 for associations, calling `#decorate` on a relation loads the records every time, regardless of whether the underlying relation has already been loaded. This is because the default of passing `all` as the collection to decorate ends up being a new relation, so even if it were previously loaded, the new relation including `all` needs to be loaded. `ActiveRecord::Associations::CollectionProxy` actually inherits from `ActiveRecord::Relation`, so by pushing this up to the relation, we can cover both scenarios. https://github.com/rails/rails/blob/57c24948eb5cc9e5f9a4cecb6f2060f53e2246e1/activerecord/lib/active_record/associations/collection_proxy.rb#L31 The documentation included in drapergem#932 (`company.products.popular.decorate`) wasn't correct as you're decorating a relation there (`popular`), not just the association (`products`). This now covers the association itself (`company.products.decorate`), as well as a relation from that association (`company.products.popular.decorate`), as well as a relation right from the class without associations (`Product.popular.decorate`). Decorating the class itself (`Product.decorate`) still calls `all` since the model class is not a relation itself.
ActiveRecord::Associations::CollectionProxy#decorateignorestarget(be it already loaded or not) and loads the association again every time it's called.That's why unsaved records get missing from the decorated collection.
Testing
it 'returns a decorated collection'record.associated_records.decorate.record.associated_records.it 'uses cached records'record.associated_records.record.associated_records.decorate.it 'caches records'record.associated_records.decorate(twice).record.associated_records.it 'respects unsaved records'buildnew associated records.record.associated_records.decorate.record.associated_recordsand the new ones.To-Dos
References