Redmine Refactor #83: Self Encapsulate Field in User

I wanted to do a refactoring that I noticed while working on my redmine_lock_users plugin. Redmine tracks the User status with a single integer field and provides some constants to access them. The problem with this is that every other class has to know and use those constants to change a status. By using self encapsulate field I was able to make the User class responsible for changing the status and all the caller has to do is to call the correct method.

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class AccountController < ApplicationController
  def activate
    # ...
    redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
    user.status = User::STATUS_ACTIVE
    # ...
  end
 
  def register_automatically(user, &block)
    # ...
    user.status = User::STATUS_ACTIVE
    user.last_login_on = Time.now
    # ...
  end
end

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class AccountController < ApplicationController
  def activate
    # ...
    redirect_to(home_url) && return unless user.registered?
    user.activate
    # ...
  end
 
  def register_automatically(user, &block)
    # ...
    user.activate
    user.last_login_on = Time.now
    # ...
  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
class User < Principal
  def activate
    self.status = STATUS_ACTIVE
  end
 
  def register
    self.status = STATUS_REGISTERED
  end
 
  def lock
    self.status = STATUS_LOCKED
  end
 
  def activate!
    update_attribute(:status, STATUS_ACTIVE)
  end
 
  def register!
    update_attribute(:status, STATUS_REGISTERED)
  end
 
  def lock!
    update_attribute(:status, STATUS_LOCKED)
  end
end

Now the caller’s code (Controller) is working at a higher level when it wants to change a user status. This helps decouple both classes so I can now modify either one to add new behavior. For example, it would be trivial to add a notification when a user is activated or locked.

Reference commit