bullet_instructure 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +20 -0
  6. data/CHANGELOG.md +75 -0
  7. data/Gemfile +19 -0
  8. data/Gemfile.mongoid +14 -0
  9. data/Gemfile.mongoid-2.4 +19 -0
  10. data/Gemfile.mongoid-2.5 +19 -0
  11. data/Gemfile.mongoid-2.6 +19 -0
  12. data/Gemfile.mongoid-2.7 +19 -0
  13. data/Gemfile.mongoid-2.8 +19 -0
  14. data/Gemfile.mongoid-3.0 +19 -0
  15. data/Gemfile.mongoid-3.1 +19 -0
  16. data/Gemfile.mongoid-4.0 +19 -0
  17. data/Gemfile.rails-3.0 +19 -0
  18. data/Gemfile.rails-3.1 +19 -0
  19. data/Gemfile.rails-3.2 +19 -0
  20. data/Gemfile.rails-4.0 +19 -0
  21. data/Gemfile.rails-4.1 +19 -0
  22. data/Guardfile +8 -0
  23. data/Hacking.md +74 -0
  24. data/MIT-LICENSE +20 -0
  25. data/README.md +428 -0
  26. data/Rakefile +52 -0
  27. data/bullet_instructure.gemspec +27 -0
  28. data/lib/bullet.rb +196 -0
  29. data/lib/bullet/active_record3.rb +148 -0
  30. data/lib/bullet/active_record3x.rb +128 -0
  31. data/lib/bullet/active_record4.rb +128 -0
  32. data/lib/bullet/active_record41.rb +121 -0
  33. data/lib/bullet/dependency.rb +81 -0
  34. data/lib/bullet/detector.rb +9 -0
  35. data/lib/bullet/detector/association.rb +67 -0
  36. data/lib/bullet/detector/base.rb +6 -0
  37. data/lib/bullet/detector/counter_cache.rb +59 -0
  38. data/lib/bullet/detector/n_plus_one_query.rb +89 -0
  39. data/lib/bullet/detector/unused_eager_loading.rb +84 -0
  40. data/lib/bullet/ext/object.rb +9 -0
  41. data/lib/bullet/ext/string.rb +5 -0
  42. data/lib/bullet/mongoid2x.rb +56 -0
  43. data/lib/bullet/mongoid3x.rb +56 -0
  44. data/lib/bullet/mongoid4x.rb +56 -0
  45. data/lib/bullet/notification.rb +10 -0
  46. data/lib/bullet/notification/base.rb +97 -0
  47. data/lib/bullet/notification/counter_cache.rb +13 -0
  48. data/lib/bullet/notification/n_plus_one_query.rb +28 -0
  49. data/lib/bullet/notification/unused_eager_loading.rb +13 -0
  50. data/lib/bullet/notification_collector.rb +24 -0
  51. data/lib/bullet/rack.rb +81 -0
  52. data/lib/bullet/registry.rb +7 -0
  53. data/lib/bullet/registry/association.rb +13 -0
  54. data/lib/bullet/registry/base.rb +40 -0
  55. data/lib/bullet/registry/object.rb +13 -0
  56. data/lib/bullet/version.rb +4 -0
  57. data/perf/benchmark.rb +121 -0
  58. data/rails/init.rb +1 -0
  59. data/spec/bullet/detector/association_spec.rb +26 -0
  60. data/spec/bullet/detector/base_spec.rb +8 -0
  61. data/spec/bullet/detector/counter_cache_spec.rb +56 -0
  62. data/spec/bullet/detector/n_plus_one_query_spec.rb +138 -0
  63. data/spec/bullet/detector/unused_eager_loading_spec.rb +88 -0
  64. data/spec/bullet/ext/object_spec.rb +17 -0
  65. data/spec/bullet/ext/string_spec.rb +13 -0
  66. data/spec/bullet/notification/base_spec.rb +83 -0
  67. data/spec/bullet/notification/counter_cache_spec.rb +12 -0
  68. data/spec/bullet/notification/n_plus_one_query_spec.rb +14 -0
  69. data/spec/bullet/notification/unused_eager_loading_spec.rb +12 -0
  70. data/spec/bullet/notification_collector_spec.rb +32 -0
  71. data/spec/bullet/rack_spec.rb +97 -0
  72. data/spec/bullet/registry/association_spec.rb +26 -0
  73. data/spec/bullet/registry/base_spec.rb +44 -0
  74. data/spec/bullet/registry/object_spec.rb +24 -0
  75. data/spec/bullet_spec.rb +41 -0
  76. data/spec/integration/active_record3/association_spec.rb +651 -0
  77. data/spec/integration/active_record4/association_spec.rb +649 -0
  78. data/spec/integration/counter_cache_spec.rb +63 -0
  79. data/spec/integration/mongoid/association_spec.rb +258 -0
  80. data/spec/models/address.rb +3 -0
  81. data/spec/models/author.rb +3 -0
  82. data/spec/models/base_user.rb +5 -0
  83. data/spec/models/category.rb +7 -0
  84. data/spec/models/city.rb +3 -0
  85. data/spec/models/client.rb +4 -0
  86. data/spec/models/comment.rb +4 -0
  87. data/spec/models/company.rb +3 -0
  88. data/spec/models/country.rb +3 -0
  89. data/spec/models/document.rb +5 -0
  90. data/spec/models/entry.rb +3 -0
  91. data/spec/models/firm.rb +4 -0
  92. data/spec/models/folder.rb +2 -0
  93. data/spec/models/mongoid/address.rb +7 -0
  94. data/spec/models/mongoid/category.rb +8 -0
  95. data/spec/models/mongoid/comment.rb +7 -0
  96. data/spec/models/mongoid/company.rb +7 -0
  97. data/spec/models/mongoid/entry.rb +7 -0
  98. data/spec/models/mongoid/post.rb +12 -0
  99. data/spec/models/mongoid/user.rb +5 -0
  100. data/spec/models/newspaper.rb +3 -0
  101. data/spec/models/page.rb +2 -0
  102. data/spec/models/person.rb +3 -0
  103. data/spec/models/pet.rb +3 -0
  104. data/spec/models/post.rb +10 -0
  105. data/spec/models/relationship.rb +4 -0
  106. data/spec/models/student.rb +3 -0
  107. data/spec/models/submission.rb +4 -0
  108. data/spec/models/teacher.rb +3 -0
  109. data/spec/models/user.rb +4 -0
  110. data/spec/models/writer.rb +2 -0
  111. data/spec/spec_helper.rb +103 -0
  112. data/spec/support/bullet_ext.rb +55 -0
  113. data/spec/support/mongo_seed.rb +65 -0
  114. data/spec/support/rack_double.rb +55 -0
  115. data/spec/support/sqlite_seed.rb +229 -0
  116. data/tasks/bullet_tasks.rake +9 -0
  117. data/test.sh +15 -0
  118. metadata +246 -0
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ if !mongoid? && active_record?
4
+ describe Bullet::Detector::CounterCache do
5
+ before(:each) do
6
+ Bullet.start_request
7
+ end
8
+
9
+ after(:each) do
10
+ Bullet.end_request
11
+ end
12
+
13
+ it "should need counter cache with all cities" do
14
+ Country.all.each do |country|
15
+ country.cities.size
16
+ end
17
+ expect(Bullet.collected_counter_cache_notifications).not_to be_empty
18
+ end
19
+
20
+ it "should not need counter cache if already define counter_cache" do
21
+ Person.all.each do |person|
22
+ person.pets.size
23
+ end
24
+ expect(Bullet.collected_counter_cache_notifications).to be_empty
25
+ end
26
+
27
+ it "should not need counter cache with only one object" do
28
+ Country.first.cities.size
29
+ expect(Bullet.collected_counter_cache_notifications).to be_empty
30
+ end
31
+
32
+ it "should not need counter cache with part of cities" do
33
+ Country.all.each do |country|
34
+ country.cities.where(:name => 'first').size
35
+ end
36
+ expect(Bullet.collected_counter_cache_notifications).to be_empty
37
+ end
38
+
39
+ context "disable" do
40
+ before { Bullet.counter_cache_enable = false }
41
+ after { Bullet.counter_cache_enable = true }
42
+
43
+ it "should not detect counter cache" do
44
+ Country.all.each do |country|
45
+ country.cities.size
46
+ end
47
+ expect(Bullet.collected_counter_cache_notifications).to be_empty
48
+ end
49
+ end
50
+
51
+ context "whitelist" do
52
+ before { Bullet.add_whitelist :type => :counter_cache, :class_name => "Country", :association => :cities }
53
+ after { Bullet.reset_whitelist }
54
+
55
+ it "should not detect counter cache" do
56
+ Country.all.each do |country|
57
+ country.cities.size
58
+ end
59
+ expect(Bullet.collected_counter_cache_notifications).to be_empty
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,258 @@
1
+ require 'spec_helper'
2
+
3
+ if mongoid?
4
+ describe Bullet::Detector::Association do
5
+ context 'embeds_many' do
6
+ context "posts => users" do
7
+ it "should detect nothing" do
8
+ Mongoid::Post.all.each do |post|
9
+ post.users.map(&:name)
10
+ end
11
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
12
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
13
+
14
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
15
+ end
16
+ end
17
+ end
18
+
19
+ context 'has_many' do
20
+ context "posts => comments" do
21
+ it "should detect non preload posts => comments" do
22
+ Mongoid::Post.all.each do |post|
23
+ post.comments.map(&:name)
24
+ end
25
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
26
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
27
+
28
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Post, :comments)
29
+ end
30
+
31
+ it "should detect preload post => comments" do
32
+ Mongoid::Post.includes(:comments).each do |post|
33
+ post.comments.map(&:name)
34
+ end
35
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
36
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
37
+
38
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
39
+ end
40
+
41
+ it "should detect unused preload post => comments" do
42
+ Mongoid::Post.includes(:comments).map(&:name)
43
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
44
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Post, :comments)
45
+
46
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
47
+ end
48
+
49
+ it "should not detect unused preload post => comments" do
50
+ Mongoid::Post.all.map(&:name)
51
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
52
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
53
+
54
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
55
+ end
56
+ end
57
+
58
+ context "category => posts, category => entries" do
59
+ it "should detect non preload with category => [posts, entries]" do
60
+ Mongoid::Category.all.each do |category|
61
+ category.posts.map(&:name)
62
+ category.entries.map(&:name)
63
+ end
64
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
65
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
66
+
67
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :posts)
68
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :entries)
69
+ end
70
+
71
+ it "should detect preload with category => posts, but not with category => entries" do
72
+ Mongoid::Category.includes(:posts).each do |category|
73
+ category.posts.map(&:name)
74
+ category.entries.map(&:name)
75
+ end
76
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
77
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
78
+
79
+ expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(Mongoid::Category, :posts)
80
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :entries)
81
+ end
82
+
83
+ it "should detect preload with category => [posts, entries]" do
84
+ Mongoid::Category.includes(:posts, :entries).each do |category|
85
+ category.posts.map(&:name)
86
+ category.entries.map(&:name)
87
+ end
88
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
89
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
90
+
91
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
92
+ end
93
+
94
+ it "should detect unused preload with category => [posts, entries]" do
95
+ Mongoid::Category.includes(:posts, :entries).map(&:name)
96
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
97
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Category, :posts)
98
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Category, :entries)
99
+
100
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
101
+ end
102
+
103
+ it "should detect unused preload with category => entries, but not with category => posts" do
104
+ Mongoid::Category.includes(:posts, :entries).each do |category|
105
+ category.posts.map(&:name)
106
+ end
107
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
108
+ expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Mongoid::Category, :posts)
109
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Category, :entries)
110
+
111
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
112
+ end
113
+ end
114
+
115
+ context "post => comment" do
116
+ it "should detect unused preload with post => comments" do
117
+ Mongoid::Post.includes(:comments).each do |post|
118
+ post.comments.first.name
119
+ end
120
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
121
+ expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Mongoid::Post, :comments)
122
+
123
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
124
+ end
125
+
126
+ it "should detect preload with post => commnets" do
127
+ Mongoid::Post.first.comments.map(&:name)
128
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
129
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
130
+
131
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
132
+ end
133
+ end
134
+
135
+ context "scope preload_comments" do
136
+ it "should detect preload post => comments with scope" do
137
+ Mongoid::Post.preload_comments.each do |post|
138
+ post.comments.map(&:name)
139
+ end
140
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
141
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
142
+
143
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
144
+ end
145
+
146
+ it "should detect unused preload with scope" do
147
+ Mongoid::Post.preload_comments.map(&:name)
148
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
149
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Post, :comments)
150
+
151
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
152
+ end
153
+ end
154
+ end
155
+
156
+ context 'belongs_to' do
157
+ context "comment => post" do
158
+ it "should detect non preload with comment => post" do
159
+ Mongoid::Comment.all.each do |comment|
160
+ comment.post.name
161
+ end
162
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
163
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
164
+
165
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Comment, :post)
166
+ end
167
+
168
+ it "should detect preload with one comment => post" do
169
+ Mongoid::Comment.first.post.name
170
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
171
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
172
+
173
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
174
+ end
175
+
176
+ it "should detect preload with comment => post" do
177
+ Mongoid::Comment.includes(:post).each do |comment|
178
+ comment.post.name
179
+ end
180
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
181
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
182
+
183
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
184
+ end
185
+
186
+ it "should not detect preload with comment => post" do
187
+ Mongoid::Comment.all.map(&:name)
188
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
189
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
190
+
191
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
192
+ end
193
+
194
+ it "should detect unused preload with comments => post" do
195
+ Mongoid::Comment.includes(:post).map(&:name)
196
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
197
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Comment, :post)
198
+
199
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
200
+ end
201
+ end
202
+ end
203
+
204
+ context "has_one" do
205
+ context "company => address" do
206
+ if Mongoid::VERSION !~ /\A3.0/
207
+ it "should detect non preload association" do
208
+ Mongoid::Company.all.each do |company|
209
+ company.address.name
210
+ end
211
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
212
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
213
+
214
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Company, :address)
215
+ end
216
+ end
217
+
218
+ it "should detect preload association" do
219
+ Mongoid::Company.includes(:address).each do |company|
220
+ company.address.name
221
+ end
222
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
223
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
224
+
225
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
226
+ end
227
+
228
+ it "should not detect preload association" do
229
+ Mongoid::Company.all.map(&:name)
230
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
231
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
232
+
233
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
234
+ end
235
+
236
+ it "should detect unused preload association" do
237
+ criteria = Mongoid::Company.includes(:address)
238
+ criteria.map(&:name)
239
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
240
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Company, :address)
241
+
242
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
243
+ end
244
+ end
245
+ end
246
+
247
+ context "call one association that in possible objects" do
248
+ it "should not detect preload association" do
249
+ Mongoid::Post.all
250
+ Mongoid::Post.first.comments.map(&:name)
251
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
252
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
253
+
254
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,3 @@
1
+ class Address < ActiveRecord::Base
2
+ belongs_to :company
3
+ end
@@ -0,0 +1,3 @@
1
+ class Author < ActiveRecord::Base
2
+ has_many :documents
3
+ end
@@ -0,0 +1,5 @@
1
+ class BaseUser < ActiveRecord::Base
2
+ has_many :comments
3
+ has_many :posts
4
+ belongs_to :newspaper
5
+ end
@@ -0,0 +1,7 @@
1
+ class Category < ActiveRecord::Base
2
+ has_many :posts
3
+ has_many :entries
4
+
5
+ has_many :submissions
6
+ has_many :users
7
+ end
@@ -0,0 +1,3 @@
1
+ class City < ActiveRecord::Base
2
+ belongs_to :country
3
+ end
@@ -0,0 +1,4 @@
1
+ class Client < ActiveRecord::Base
2
+ has_many :relationships
3
+ has_many :firms, through: :relationships
4
+ end
@@ -0,0 +1,4 @@
1
+ class Comment < ActiveRecord::Base
2
+ belongs_to :post, inverse_of: :comments
3
+ belongs_to :author, class_name: "BaseUser"
4
+ end
@@ -0,0 +1,3 @@
1
+ class Company < ActiveRecord::Base
2
+ has_one :address
3
+ end
@@ -0,0 +1,3 @@
1
+ class Country < ActiveRecord::Base
2
+ has_many :cities
3
+ end
@@ -0,0 +1,5 @@
1
+ class Document < ActiveRecord::Base
2
+ has_many :children, class_name: "Document", foreign_key: 'parent_id'
3
+ belongs_to :parent, class_name: "Document", foreign_key: 'parent_id'
4
+ belongs_to :author
5
+ end
@@ -0,0 +1,3 @@
1
+ class Entry < ActiveRecord::Base
2
+ belongs_to :category
3
+ end
@@ -0,0 +1,4 @@
1
+ class Firm < ActiveRecord::Base
2
+ has_many :relationships
3
+ has_many :clients, through: :relationships
4
+ end
@@ -0,0 +1,2 @@
1
+ class Folder < Document
2
+ end
@@ -0,0 +1,7 @@
1
+ class Mongoid::Address
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ belongs_to :company, :class_name => "Mongoid::Company"
7
+ end
@@ -0,0 +1,8 @@
1
+ class Mongoid::Category
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ has_many :posts, :class_name => "Mongoid::Post"
7
+ has_many :entries, :class_name => "Mongoid::Entry"
8
+ end
@@ -0,0 +1,7 @@
1
+ class Mongoid::Comment
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ belongs_to :post, :class_name => "Mongoid::Post"
7
+ end
@@ -0,0 +1,7 @@
1
+ class Mongoid::Company
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ has_one :address, :class_name => "Mongoid::Address"
7
+ end
@@ -0,0 +1,7 @@
1
+ class Mongoid::Entry
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ belongs_to :category, :class_name => "Mongoid::Category"
7
+ end
@@ -0,0 +1,12 @@
1
+ class Mongoid::Post
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ has_many :comments, :class_name => "Mongoid::Comment"
7
+ belongs_to :category, :class_name => "Mongoid::Category"
8
+
9
+ embeds_many :users, :class_name => "Mongoid::User"
10
+
11
+ scope :preload_comments, lambda { includes(:comments) }
12
+ end