Redmine Refactor #144: Convert WikiController to a REST resource

I finally felt comfortable enough to do the final conversion of WikiController to a REST resource. Yesterday’s refactoring of the :id parameter make this a lot easier.

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
# config/routes.rb
  map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
  map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
  map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
  map.with_options :controller => 'wiki' do |wiki_routes|
    wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
      wiki_views.connect 'projects/:project_id/wiki/export', :action => 'export'
      wiki_views.connect 'projects/:project_id/wiki/index', :action => 'index'
      wiki_views.connect 'projects/:project_id/wiki/date_index', :action => 'date_index'
      wiki_views.connect 'projects/:project_id/wiki/:id', :action => 'show', :id => nil
      wiki_views.connect 'projects/:project_id/wiki/:id/edit', :action => 'edit'
      wiki_views.connect 'projects/:project_id/wiki/:id/rename', :action => 'rename'
      wiki_views.connect 'projects/:project_id/wiki/:id/history', :action => 'history'
      wiki_views.connect 'projects/:project_id/wiki/:id/diff/:version/vs/:version_from', :action => 'diff'
      wiki_views.connect 'projects/:project_id/wiki/:id/annotate/:version', :action => 'annotate'
    end
 
    wiki_routes.connect 'projects/:project_id/wiki/:id/:action', 
      :action => /rename|preview|protect|add_attachment/,
      :conditions => {:method => :post}
 
    wiki_routes.connect 'projects/:project_id/wiki/:id/edit', :action => 'update', :conditions => {:method => :post}
 
    wiki_routes.connect 'projects/:project_id/wiki/:id', :action => 'destroy', :conditions => {:method => :delete}
  end
 
  # Project routes
  map.resources :projects, :member => {
    :copy => [:get, :post],
    :settings => :get,
    :modules => :post,
    :archive => :post,
    :unarchive => :post
  } do |project|
    project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy]
    project.resources :files, :only => [:index, :new, :create]
    project.resources :versions, :collection => {:close_completed => :put}, :member => {:status_by => :post}
    project.resources :news, :shallow => true
    project.resources :time_entries, :controller => 'timelog', :path_prefix => 'projects/:project_id'
 
 
  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
# config/routes.rb
  map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
  map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
  map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
  # No more 'wiki' routing
 
  # Project routes
  map.resources :projects, :member => {
    :copy => [:get, :post],
    :settings => :get,
    :modules => :post,
    :archive => :post,
    :unarchive => :post
  } do |project|
    project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy]
    project.resources :files, :only => [:index, :new, :create]
    project.resources :versions, :collection => {:close_completed => :put}, :member => {:status_by => :post}
    project.resources :news, :shallow => true
    project.resources :time_entries, :controller => 'timelog', :path_prefix => 'projects/:project_id'
 
    project.wiki_start_page 'wiki', :controller => 'wiki', :action => 'show', :conditions => {:method => :get}
    project.wiki_index 'wiki/index', :controller => 'wiki', :action => 'index', :conditions => {:method => :get}
    project.wiki_diff 'wiki/:id/diff/:version/vs/:version_from', :controller => 'wiki', :action => 'diff'
    project.wiki_annotate 'wiki/:id/annotate/:version', :controller => 'wiki', :action => 'annotate'
    project.resources :wiki, :except => [:new, :create], :member => {
      :rename => [:get, :post],
      :history => :get,
      :preview => :any,
      :protect => :post,
      :add_attachment => :post
    }, :collection => {
      :export => :get,
      :date_index => :get
    }
 
  end

Because of the odd routing in WikiController, I had to add some additional routes so everything would work.

Wiki Start Page

Redmine has a concept of a Wiki Start Page. This page is the default page that is shown when first browsing to a wiki. In reality though, the page has an title but it’s masked in the url routing. To handle it, I used the project_wiki_start_page named route and pointed it to the :show action.

Wiki Index

Since the Wiki Start Page takes over the path for standard #index action for the resource, a second route has to be defined to handle the #index. This departs from the Rails conventions but I can’t think of a better way to handle this.

Wiki Diff and Wiki Annotate

Custom routes had to be added to support the pretty urls for showing diffs and annotations of wiki pages. Since they are only views on the resource, they are quite simple.

Custom member and collection actions

Since WikiController still has a bunch of actions, I had to define them in the resource definition so their routes can be generated. Eventually, most of these should be refactored to other controllers or merged into the standard actions.

With this refactoring, I now have enough of Redmine’s controllers converted to REST resources so that I can start working on Redmine 1.1’s new APIs. I think I’ll be taking a break for tomorrow’s refactoring while I think about what my next steps are.

  • continue this refactoring series and start refactoring the controller methods
  • continue this refactoring series and start refactoring other controllers to convert them to resources (Redmine 1.2’s APIs)
  • pause refactoring and do some posts on implementing Redmine’s new APIs
  • or some other idea that I haven’t thought about yet

What would you be interested in reading about?

Reference commit

2 comments

  1. Rob says:

    Thanks for these interesting posts on refactoring.
    You might like to correct the typo (were -> where) in your “About” signature.

Comments are closed.