Redmine Refactor #110: Convert ProjectsController’s routes to resources

This is the first of the big refactorings I’ve been working towards. Like refactor #100, this required a lot of work to keep everything working until the rest of the controller refactorings are complete.

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
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.with_options :controller => 'projects' do |projects|
    projects.with_options :conditions => {:method => :get} do |project_views|
      project_views.connect 'projects', :action => 'index'
      project_views.connect 'projects.:format', :action => 'index'
      project_views.connect 'projects/new', :action => 'new'
      project_views.connect 'projects/:id', :action => 'show'
      project_views.connect 'projects/:id.:format', :action => 'show'
      project_views.connect 'projects/:id/:action', :action => /destroy|settings/
      project_views.connect 'projects/:id/files', :controller => 'files', :action => 'index'
      project_views.connect 'projects/:id/files/new', :controller => 'files', :action => 'new'
      project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
      project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new'
    end
 
    projects.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity|
      activity.connect 'projects/:id/activity'
      activity.connect 'projects/:id/activity.:format'
      activity.connect 'activity', :id => nil
      activity.connect 'activity.:format', :id => nil
    end
 
    projects.with_options :conditions => {:method => :post} do |project_actions|
      project_actions.connect 'projects/new', :action => 'create'
      project_actions.connect 'projects', :action => 'create'
      project_actions.connect 'projects.:format', :action => 'create', :format => /xml/
      project_actions.connect 'projects/:id/edit', :action => 'update'
      project_actions.connect 'projects/:id/:action', :action => /destroy|archive|unarchive/
      project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new'
      project_actions.connect 'projects/:id/activities/save', :controller => 'project_enumerations', :action => 'save'
    end
 
    projects.with_options :conditions => {:method => :put} do |project_actions|
      project_actions.conditions 'projects/:id.:format', :action => 'update', :format => /xml/
    end
 
    projects.with_options :conditions => {:method => :delete} do |project_actions|
      project_actions.conditions 'projects/:id.:format', :action => 'destroy', :format => /xml/
      project_actions.conditions 'projects/:id/reset_activities', :controller => 'project_enumerations', :action => 'destroy'
    end
  end
end

After

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
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.resources :projects, :member => {
    :copy => [:get, :post],
    :settings => :get,
    :modules => :post,
    :archive => :post,
    :unarchive => :post
  }
 
  # Destroy uses a get request to prompt the user before the actual DELETE request
  map.project_destroy_confirm 'projects/:id/destroy', :controller => 'projects', :action => 'destroy', :conditions => {:method => :get}
 
  # TODO: port to be part of the resources route(s)
  map.with_options :controller => 'projects' do |project_mapper|
    project_mapper.with_options :conditions => {:method => :get} do |project_views|
      project_views.connect 'projects/:id/files', :controller => 'files', :action => 'index'
      project_views.connect 'projects/:id/files/new', :controller => 'files', :action => 'new'
      project_views.connect 'projects/:id/settings/:tab', :controller => 'projects', :action => 'settings'
      project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new'
    end
 
    project_mapper.with_options :conditions => {:method => :post} do |project_actions|
      project_actions.connect 'projects/:id/files/new', :controller => 'files', :action => 'new'
      project_actions.connect 'projects/:id/activities/save', :controller => 'project_enumerations', :action => 'save'
    end
 
    project_mapper.with_options :conditions => {:method => :delete} do |project_actions|
      project_actions.conditions 'projects/:id/reset_activities', :controller => 'project_enumerations', :action => 'destroy'
    end
 
  end
 
  map.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity|
    activity.connect 'projects/:id/activity'
    activity.connect 'projects/:id/activity.:format'
    activity.connect 'activity', :id => nil
    activity.connect 'activity.:format', :id => nil
  end
 
end

Walking through this refactoring, the first thing I did was to create a map.resources for projects. This will handle most of the CRUD actions for ProjectsController. The extra :member options will also take care of the remaining non-REST routes.

After the resources, I had to keep around some of the miscellaneous routes that were defined. It looks like a few of these could be converted into new resources underneath projects, like ProjectEnumerations and Files. I also think it’s about time to merge the issues routes with projects.

Reference commit