在我们正在构建的当前Rails应用程序上,用户可以有公司范围的偏好、用户特定的偏好或项目特定的偏好。当用户创建采购订单时,我需要聚合所有这些首选项,并在订单上设置相应的属性。我从"项目首选项"开始,一直向上遍历层次结构,直到收集到以下所有属性。
class Preferences
def initialize(project, user)
@preferences = Preferences.new
@project = project
@user = user
end
def stage
end
def set_preferences
set_department
end
End
class UserPreferences < Preferences
def initialize(project, user)
super(project, user)
@user_preferences = nil
end
def stage
@user_preferences = @user.preferences
set_preferences if @user_preferences.present?
super unless @preferences.valid?
end
def set_department
@preferences ||= @user_preferences.dept
end
end
class ProjectPreferences < UserPreferences
def initialize(project, user)
super(project, user)
@project_preferences = nil
end
def stage
@project_preferences = @project.preferences
set_preferences if @project_preferences.present?
super unless @preferences.valid?
end
def set_department
@preferences.dept ||= @project_preferences.dept
end
end
def StagePreferences < ProjectPreferences
def initialize(project, user)
super(project, user)
end
def stage
super
@preferences
end
end
该方法的调用方式如下
StagePreferences.new(project, current_user).stage
我遇到了一个问题,当我从UserPreferences类调用set_preferences方法时(因为没有为此项目设置首选项),会调用UserPreferences的set_department,返回如下错误?
NoMethodError: undefined method `dept' for nil:NilClass
from /home/rails/tools/ss/lib/preferences/project_preferences.rb:27:in `set_department'
from /home/rails/tools/ss/lib/preferences/preferences.rb:26:in `set_preferences'
from /home/rails/tools/ss/lib/preferences/user_preferences.rb:17:in `stage'
from /home/rails/tools/ss/lib/preferences/stage_preferences.rb:23:in `stage'
from /home/rails/tools/ss/lib/preferences/project_preferences.rb:17:in `stage'
from /home/rails/tools/ss/lib/preferences/stage_preferences.rb:8:in `stage'
from (irb):8
from /home/rails/.rvm/gems/ruby-2.2.1/gems/railties-4.0.13/lib/rails/commands/console.rb:90:in `start'
from /home/rails/.rvm/gems/ruby-2.2.1/gems/railties-4.0.13/lib/rails/commands/console.rb:9:in `start'
from /home/rails/.rvm/gems/ruby-2.2.1/gems/railties-4.0.13/lib/rails/commands.rb:62:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
Ruby的动态调度使用了对接收器上的方法的"深度优先"搜索,这意味着你的ProjectPreferences#set_department
方法将始终被调用,而你的"UserPreferences#set_department"方法永远不会被调用(仅供参考,看起来你在这个方法中有拼写错误?)。
可以在当前层次结构中使用Ruby的instance_method
方法通过:调用此方法
UserPreferences.instance_method(:set_department).bind(self).call
这允许你在self上调用这个"不可访问"的方法,就好像ProjectPreferences没有覆盖它一样
然而,我会质疑拥有这么多Preference子类的要求,正如这个问题所强调的那样,类层次结构中的调试问题变得越来越复杂。