• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MushroomObserver / mushroom-observer / 14954570905

11 May 2025 09:46AM UTC coverage: 94.716% (+0.006%) from 94.71%
14954570905

Pull #2966

github

nimmolo
Update comment.rb
Pull Request #2966: Fix comment broadcasting

19 of 19 new or added lines in 4 files covered. (100.0%)

3 existing lines in 1 file now uncovered.

34059 of 35959 relevant lines covered (94.72%)

490.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.11
/app/controllers/comments_controller.rb
1
# frozen_string_literal: true
2

3
#  ==== Show, CRUD actions
4
#  index::
5
#  show::
6
#  new::
7
#  create::
8
#  edit::
9
#  update::
10
#  destroy::
11
#
12
class CommentsController < ApplicationController
1✔
13
  before_action :login_required
1✔
14
  before_action :pass_query_params, except: [:index]
1✔
15

16
  # Bullet doesn't seem to be able to figure out that we cannot eager load
17
  # through polymorphic relations, so I'm just disabling it for these actions.
18
  around_action :skip_bullet, if: -> { defined?(Bullet) }, only: [:index]
19✔
19

20
  ##############################################################################
21
  # INDEX
22
  #
23
  def index
1✔
24
    build_index_with_query
18✔
25
  end
26

27
  private
1✔
28

29
  def default_sort_order
1✔
30
    ::Query::Comments.default_order # :created_at
3✔
31
  end
32

33
  # ApplicationController uses this table to dispatch #index to a private method
34
  def index_active_params
1✔
35
    [:target, :pattern, :by_user, :for_user, :by].freeze
33✔
36
  end
37

38
  # Show selected list of comments, based on current Query.
39
  # (Linked from show_comment, next to "prev" and "next"... or will be.)
40
  # Passes explicit :by param to affect title (only).
41
  def sorted_index_opts
1✔
42
    sorted_by = params[:by] || default_sort_order
1✔
43
    super.merge(query_args: { order_by: sorted_by })
1✔
44
  end
45

46
  # Shows comments by a given user, most recent first. (Linked from show_user.)
47
  def by_user
1✔
48
    user = find_obj_or_goto_index(
4✔
49
      model: User, obj_id: params[:by_user].to_s,
50
      index_path: comments_path
51
    )
52
    return unless user
4✔
53

54
    query = create_query(:Comment, by_users: user)
3✔
55
    [query, {}]
3✔
56
  end
57

58
  # Shows comments for a given user's Observations, most recent first.
59
  # (Linked from show_user.)
60
  def for_user
1✔
61
    user = find_obj_or_goto_index(
4✔
62
      model: User, obj_id: params[:for_user].to_s,
63
      index_path: comments_path
64
    )
65
    return unless user
4✔
66

67
    query = create_query(:Comment, for_user: user)
3✔
68
    [query, {}]
3✔
69
  end
70

71
  # Shows comments for a given object, most recent first. (Linked from the
72
  # "and more..." thingy at the bottom of truncated embedded comment lists.)
73
  def target
1✔
74
    return no_model unless (model = Comment.safe_model_from_name(params[:type]))
4✔
75
    return unless (target = find_or_goto_index(model, params[:target].to_s))
2✔
76

77
    query = create_query(:Comment, target: { type: target.class.name,
2✔
78
                                             id: target.id })
79
    [query, {}]
2✔
80
  end
81

82
  def no_model
1✔
83
    flash_error(:runtime_invalid.t(type: '"type"', value: params[:type].to_s))
2✔
84
    redirect_back_or_default(action: :index)
2✔
85
    [nil, {}]
2✔
86
  end
87

88
  def index_display_opts(opts, query)
1✔
89
    opts = {
90
      num_per_page: 25,
13✔
91
      # (Eager-loading of names might fail when comments start to apply to
92
      # objects other than observations.)
93
      include: [:target, :user]
94
    }.merge(opts)
95

96
    # Paginate by letter if sorting by user.
97
    if %w[user reverse_user].include?(query.params[:order_by])
13✔
98
      opts[:letters] = true
1✔
99
    end
100

101
    @full_detail = query.params[:target].present?
13✔
102

103
    opts
13✔
104
  end
105

106
  public
1✔
107

108
  ##############################################################################
109
  #
110
  #  :section: Show
111
  #
112
  ##############################################################################
113

114
  # Display comment by itself.
115
  # Linked from: show_<object>, index
116
  # Inputs: params[:id] (comment)
117
  # Outputs: @comment, @object
118
  def show
1✔
119
    store_location
1✔
120
    return unless (@comment = find_comment!)
1✔
121

122
    case params[:flow]
1✔
123
    when "next"
124
      redirect_to_next_object(:next, Comment, params[:id]) and return
×
125
    when "prev"
126
      redirect_to_next_object(:prev, Comment, params[:id]) and return
×
127
    end
128

129
    @target = @comment.target
1✔
130
    allowed_to_see!(@target)
1✔
131
  end
132

133
  private
1✔
134

135
  def find_comment!
1✔
136
    find_or_goto_index(Comment, params[:id].to_s)
7✔
137
  end
138

139
  # Make sure users can't see/add comments on objects they aren't allowed to
140
  # view!  Redirect and return +false+ if they can't, else return +true+.
141
  def allowed_to_see!(object)
1✔
142
    return true unless object.respond_to?(:is_reader?) &&
12✔
143
                       !object.is_reader?(@user) &&
144
                       !in_admin_mode?
145

146
    flash_error(:runtime_show_description_denied.t)
1✔
147
    parent = object.parent
1✔
148
    redirect_to(controller: parent.show_controller,
1✔
149
                action: parent.show_action, id: parent.id)
150
    false
1✔
151
  end
152

153
  public
1✔
154

155
  ##############################################################################
156
  #
157
  #  :section: CRUD actions
158
  #
159
  ##############################################################################
160

161
  # Form to create comment for an object.
162
  # Linked from: show_<object>
163
  # Inputs:
164
  #   params[:id] (object id)
165
  #   params[:type] (object type)
166
  #   params[:comment][:summary]
167
  #   params[:comment][:comment]
168
  # Success:
169
  #   Redirects to show_<object>.
170
  # Failure:
171
  #   Renders add_comment again.
172
  #   Outputs: @comment, @object
173
  def new
1✔
174
    return unless (@target = load_target(params[:type], params[:target])) &&
6✔
175
                  allowed_to_see!(@target)
176

177
    @comment = Comment.new(target: @target)
4✔
178

179
    respond_to do |format|
4✔
180
      format.html
4✔
181
      format.turbo_stream { render_modal_comment_form }
4✔
182
    end
183
  end
184

185
  def create
1✔
186
    return unless (@target = load_target(params[:type], params[:target])) &&
2✔
187
                  allowed_to_see!(@target)
188

189
    @comment = Comment.new(target: @target)
2✔
190
    @comment.attributes = permitted_comment_params if params[:comment]
2✔
191

192
    unless @comment.save
2✔
193
      flash_object_errors(@comment)
×
194
      reload_form and return
×
195
    end
196

197
    @comment.log_create
2✔
198
    flash_notice(:runtime_form_comments_create_success.t(id: @comment.id))
2✔
199

200
    refresh_comments_or_redirect_to_show
2✔
201
  end
202

203
  # Form to edit a comment for an object..
204
  # Linked from: show_comment, show_object.
205
  # Inputs:
206
  #   params[:id]
207
  #   params[:comment][:summary]
208
  #   params[:comment][:comment]
209
  # Success:
210
  #   Redirects to show_object.
211
  # Failure:
212
  #   Renders edit_comment again.
213
  #   Outputs: @comment, @object
214
  def edit
1✔
215
    return unless (@comment = find_comment!)
2✔
216

217
    @target = comment_target
2✔
218
    return unless allowed_to_see!(@target)
2✔
219
    return unless check_permission_or_redirect!(@comment, @target)
2✔
220

221
    respond_to do |format|
1✔
222
      format.turbo_stream { render_modal_comment_form }
1✔
223
      format.html
1✔
224
    end
225
  end
226

227
  def update
1✔
228
    return unless (@comment = find_comment!)
2✔
229

230
    @target = comment_target
2✔
231
    return unless allowed_to_see!(@target) &&
2✔
232
                  check_permission_or_redirect!(@comment, @target)
233

234
    @comment.attributes = permitted_comment_params if params[:comment]
1✔
235
    reload_form and return unless comment_updated?
1✔
236

237
    refresh_comments_or_redirect_to_show
1✔
238
  end
239

240
  # Callback to destroy a comment.
241
  # Linked from: show_comment, show_object.
242
  # Redirects to show_object.
243
  # Inputs: params[:id]
244
  # Outputs: none
245
  def destroy
1✔
246
    return unless (@comment = find_comment!)
2✔
247

248
    @target = @comment.target
2✔
249
    if !check_permission!(@comment)
2✔
250
      # all html requests redirect to object show page
251
    elsif !@comment.destroy
1✔
252
      flash_error(:runtime_form_comments_destroy_failed.t(id: params[:id]))
×
253
    else
254
      @comment.log_destroy
1✔
255
      flash_notice(:runtime_form_comments_destroy_success.t(id: params[:id]))
1✔
256
    end
257

258
    refresh_comments_or_redirect_to_show
2✔
259
  end
260

261
  private
1✔
262

263
  # The identifier needs to be more specific for an edit form, because
264
  # we give users the option to edit any number of their own comments on a
265
  # show page. "comment" disambiguates :new, because :edit always has id
266
  def render_modal_comment_form
1✔
267
    render(partial: "shared/modal_form",
×
268
           locals: { title: modal_title, identifier: modal_identifier,
269
                     form: "comments/form" }) and return
270
  end
271

272
  def reload_modal_form
1✔
273
    render(partial: "shared/modal_form_reload",
×
274
           locals: { identifier: modal_identifier,
275
                     form: "comments/form" })
276
  end
277

278
  def modal_identifier
1✔
279
    case action_name
×
280
    when "new", "create"
281
      "comment"
×
282
    when "edit", "update"
283
      "comment_#{@comment.id}"
×
284
    end
285
  end
286

287
  def modal_title
1✔
288
    case action_name
×
289
    when "new", "create"
290
      helpers.comment_form_new_title(target: @target)
×
291
    when "edit", "update"
292
      helpers.comment_form_edit_title(target: @target)
×
293
    end
294
  end
295

296
  def permitted_comment_params
1✔
297
    params[:comment].permit([:summary, :comment])
3✔
298
  end
299

300
  def reload_form
1✔
301
    respond_to do |format|
×
302
      format.turbo_stream { reload_modal_form }
×
303
      format.html { render(:new) }
×
304
    end
305
  end
306

307
  def refresh_comments_or_redirect_to_show
1✔
308
    # Comment broadcasts are sent from the model
309
    respond_to do |format|
5✔
310
      format.turbo_stream { head(:ok) } # do
5✔
311
      #   case action_name
312
      #   when "create", "update"
313
      #     render(turbo_stream: turbo_stream.prepend("comments", @comment))
314
      #   when "destroy"
315
      #     render(turbo_stream: turbo_stream.remove(@comment))
316
      #   end
317
      # end
318
      format.html do
5✔
319
        redirect_with_query(controller: @target.show_controller,
5✔
320
                            action: @target.show_action, id: @target.id)
321
      end
322
    end
323
  end
324

325
  def comment_target
1✔
326
    load_target(@comment.target_type, @comment.target_id)
4✔
327
  end
328

329
  def load_target(type, id)
1✔
330
    case type
12✔
331
    when "Observation"
332
      load_for_show_observation_or_goto_index(id)
6✔
333
    else
334
      target = Comment.find_object(type, id.to_s)
6✔
335
      redirect_back_or_default("/") unless target
6✔
336
      target
6✔
337
    end
338
  end
339

340
  def check_permission_or_redirect!(comment, target)
1✔
341
    return true if check_permission!(comment)
4✔
342

343
    redirect_with_query(controller: target.show_controller,
2✔
344
                        action: target.show_action, id: target.id)
345
    false
2✔
346
  end
347

348
  def comment_updated?
1✔
349
    if !@comment.changed?
1✔
350
      flash_notice(:runtime_no_changes.t)
×
351
      # changing this to `false`, because comment has not been changed.
352
      # Flash should render in modal (the `false` path) - AN 20231201
UNCOV
353
      false
×
354
    elsif !@comment.save
1✔
UNCOV
355
      flash_object_errors(@comment)
×
UNCOV
356
      false
×
357
    else
358
      @comment.log_update
1✔
359
      flash_notice(:runtime_form_comments_edit_success.t(id: @comment.id))
1✔
360
      true
1✔
361
    end
362
  end
363
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc