Redmine Refactor #101: Extract activity from ProjectsController to a new controller

Starting on my refactoring of Redmine’s ProjectsController, I used extract class to move the #activity method to a new controller. #activity is used to get a timeline of events on a project, so you can see what’s recently happened. Since it’s not tied to an actual project record, it doesn’t fit on ProjectsController.

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class ProjectsController  :activity
 
  before_filter :find_optional_project, :only => :activity
  accept_key_auth :activity, :index
 
  def activity
    @days = Setting.activity_days_default.to_i
 
    if params[:from]
      begin; @date_to = params[:from].to_date + 1; rescue; end
    end
 
    @date_to ||= Date.today + 1
    @date_from = @date_to - @days
    @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
    @author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
 
    @activity = Redmine::Activity::Fetcher.new(User.current, :project => @project, 
                                                             :with_subprojects => @with_subprojects,
                                                             :author => @author)
    @activity.scope_select {|t| !params["show_#{t}"].nil?}
    @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
 
    events = @activity.events(@date_from, @date_to)
 
    if events.empty? || stale?(:etag => [events.first, User.current])
      respond_to do |format|
        format.html { 
          @events_by_day = events.group_by(&:event_date)
          render :layout => false if request.xhr?
        }
        format.atom {
          title = l(:label_activity)
          if @author
            title = @author.name
          elsif @activity.scope.size == 1
            title = l("label_#{@activity.scope.first.singularize}_plural")
          end
          render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
        }
      end
    end
 
  rescue ActiveRecord::RecordNotFound
    render_404
  end
end

After

1
2
3
class ProjectsController < ApplicationController
  accept_key_auth :index
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class ActivitiesController  @project, 
                                                             :with_subprojects => @with_subprojects,
                                                             :author => @author)
    @activity.scope_select {|t| !params["show_#{t}"].nil?}
    @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
 
    events = @activity.events(@date_from, @date_to)
 
    if events.empty? || stale?(:etag => [events.first, User.current])
      respond_to do |format|
        format.html { 
          @events_by_day = events.group_by(&:event_date)
          render :layout => false if request.xhr?
        }
        format.atom {
          title = l(:label_activity)
          if @author
            title = @author.name
          elsif @activity.scope.size == 1
            title = l("label_#{@activity.scope.first.singularize}_plural")
          end
          render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
        }
      end
    end
 
  rescue ActiveRecord::RecordNotFound
    render_404
  end
 
  private
 
  # TODO: refactor, duplicated in projects_controller
  def find_optional_project
    return true unless params[:id]
    @project = Project.find(params[:id])
    authorize
  rescue ActiveRecord::RecordNotFound
    render_404
  end
 
end

Other than the method move and rename, there isn’t that much to this refactoring.

The #find_optional_project method had to be copied over from ProjectsController since it was used as a before_filter, but I find that’s common when a controller is first split. Like other code smells I create intentionally, I used a comment with a “TODO” to mark it for later.

There are still quite a few methods in ProjectsController to be refactored. From the looks of it, some will end up in new controllers like this refactoring. While others will be merged into other actions.

Reference commit