The Refactoring
Today is the day that I finally get to finish the set of refactorings I started back on the 24th. My goal was to start converting the IssuesController
into a skinny controller by moving code into the models. Today, I finally was able to cleanly move #issue_update
from the controller into the Issue model.
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
# app/controllers/issues_controller.rb
def edit
update_issue_from_params
@journal = @issue.current_journal
respond_to do |format|
format.html { }
format.xml { }
end
end
def update
update_issue_from_params
if issue_update
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.xml { head :ok }
end
else
@journal = @issue.current_journal
respond_to do |format|
format.html { render :action => 'edit' }
format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
# Remove the previously added attachments if issue was not updated
attachments[:files].each(&:destroy) if attachments[:files]
end
def issue_update
if params[:time_entry] && params[:time_entry][:hours].present? && User.current.allowed_to?(:log_time, @project)
@time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
@issue.time_entries << @time_entry
end
if @issue.valid?
attachments = Attachment.attach_files(@issue, params[:attachments])
render_attachment_warning_if_needed(@issue)
attachments[:files].each {|a| @issue.current_journal.details < 'attachment', :prop_key => a.id, :value => a.filename)}
call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => @issue.current_journal})
if @issue.save
if !@issue.current_journal.new_record?
# Only send notification if something was actually changed
flash[:notice] = l(:notice_successful_update)
end
call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => @issue.current_journal})
return true
end
end
# failure, returns false
end
end |
# app/controllers/issues_controller.rb
def edit
update_issue_from_params
@journal = @issue.current_journal
respond_to do |format|
format.html { }
format.xml { }
end
end
def update
update_issue_from_params
if issue_update
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.xml { head :ok }
end
else
@journal = @issue.current_journal
respond_to do |format|
format.html { render :action => 'edit' }
format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
# Remove the previously added attachments if issue was not updated
attachments[:files].each(&:destroy) if attachments[:files]
end
def issue_update
if params[:time_entry] && params[:time_entry][:hours].present? && User.current.allowed_to?(:log_time, @project)
@time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
@issue.time_entries << @time_entry
end
if @issue.valid?
attachments = Attachment.attach_files(@issue, params[:attachments])
render_attachment_warning_if_needed(@issue)
attachments[:files].each {|a| @issue.current_journal.details < 'attachment', :prop_key => a.id, :value => a.filename)}
call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => @issue.current_journal})
if @issue.save
if !@issue.current_journal.new_record?
# Only send notification if something was actually changed
flash[:notice] = l(:notice_successful_update)
end
call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => @issue.current_journal})
return true
end
end
# failure, returns false
end
end
1
2
3
4
|
# app/models/issue.rb
class Issue < ActiveRecord::Base
# ...
end |
# app/models/issue.rb
class Issue < ActiveRecord::Base
# ...
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
|
# app/controllers/issues_controller.rb
class IssuesController 'show', :id => @issue}) }
format.xml { head :ok }
end
else
render_attachment_warning_if_needed(@issue)
if !@issue.current_journal.new_record?
# Only send notification if something was actually changed
flash[:notice] = l(:notice_successful_update)
end
@journal = @issue.current_journal
respond_to do |format|
format.html { render :action => 'edit' }
format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
# Remove the previously added attachments if issue was not updated
attachments[:files].each(&:destroy) if attachments[:files]
end
end |
# app/controllers/issues_controller.rb
class IssuesController 'show', :id => @issue}) }
format.xml { head :ok }
end
else
render_attachment_warning_if_needed(@issue)
if !@issue.current_journal.new_record?
# Only send notification if something was actually changed
flash[:notice] = l(:notice_successful_update)
end
@journal = @issue.current_journal
respond_to do |format|
format.html { render :action => 'edit' }
format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
# Remove the previously added attachments if issue was not updated
attachments[:files].each(&:destroy) if attachments[:files]
end
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
|
# app/models/issue.rb
class Issue < ActiveRecord::Base
# Saves an issue, time_entry, attachments, and a journal from the parameters
def save_issue_with_child_records(params, existing_time_entry=nil)
if params[:time_entry] && params[:time_entry][:hours].present? && User.current.allowed_to?(:log_time, project)
@time_entry = existing_time_entry || TimeEntry.new
@time_entry.project = project
@time_entry.issue = self
@time_entry.user = User.current
@time_entry.spent_on = Date.today
@time_entry.attributes = params[:time_entry]
self.time_entries << @time_entry
end
if valid?
attachments = Attachment.attach_files(self, params[:attachments])
attachments[:files].each {|a| @current_journal.details < 'attachment', :prop_key => a.id, :value => a.filename)}
# TODO: Rename hook
Redmine::Hook.call_hook(:controller_issues_edit_before_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
if save
# TODO: Rename hook
Redmine::Hook.call_hook(:controller_issues_edit_after_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
return true
end
end
# failure, returns false
end
end |
# app/models/issue.rb
class Issue < ActiveRecord::Base
# Saves an issue, time_entry, attachments, and a journal from the parameters
def save_issue_with_child_records(params, existing_time_entry=nil)
if params[:time_entry] && params[:time_entry][:hours].present? && User.current.allowed_to?(:log_time, project)
@time_entry = existing_time_entry || TimeEntry.new
@time_entry.project = project
@time_entry.issue = self
@time_entry.user = User.current
@time_entry.spent_on = Date.today
@time_entry.attributes = params[:time_entry]
self.time_entries << @time_entry
end
if valid?
attachments = Attachment.attach_files(self, params[:attachments])
attachments[:files].each {|a| @current_journal.details < 'attachment', :prop_key => a.id, :value => a.filename)}
# TODO: Rename hook
Redmine::Hook.call_hook(:controller_issues_edit_before_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
if save
# TODO: Rename hook
Redmine::Hook.call_hook(:controller_issues_edit_after_save, { :params => params, :issue => self, :time_entry => @time_entry, :journal => @current_journal})
return true
end
end
# failure, returns false
end
end
Review
In order to complete this move I had move some of the code for setting the flash messages into the action, but that’s fine by me. The resulting code is a bit bigger than when it was in the controller, but I feel that it’s in a place where it can be better used now.
The other pending problem is that with this move, the plugin hooks are now misnamed (Redmine::Hook.call_hook
). I know there are plugins using them so I don’t want to just rename them and break the code. I’m thinking about adding a new Hook method for deprecated hooks that will let them continue to function but warn the plugin developers that they will be removed later. Has anyone else working with deprecating an API? How did you do it?
Reference commit