【问题标题】:Refactoring view logic in Rails在 Rails 中重构视图逻辑
【发布时间】:2011-11-23 19:39:21
【问题描述】:

这是我需要做的。我有一个Tournament 模型,它通过Signup (N:N) 连接到User。

Signup 添加的唯一内容是注册状态。锦标赛有开始时间,用户只能在锦标赛开始前 60 分钟前注册。之后,注册用户就可以签入了。所以基本上我有两个状态选项

简而言之,模型看起来像这样

class Signup < ActiveRecord::Base
  REGISTERED = 0
  CHECKED = 1

  belongs_to :tournament
  belongs_to :user
end

class Tournament < ActiveRecord::Base
  has_many :signups
  has_many :users, :through => :signups
end

class User < ActiveRecord::Base  
  has_many :signups
  has_many :tournaments, :through => :signups
end

为了简短起见,我跳过了一些代码。问题出在视图上,因为我要记住很多条件。这是我的实际代码 (using Slim as a templating engine)

- if logged_in?
  - if current_user.registered_for?(@tournament)
    - if @tournament.starts_at < 60.minutes.from_now
      p Signups are closed, only registered users can now check in
      - if current_user.registered_for?(@tournament)
        = button_to "Checkin!", { :controller => :signups, :action => :update, :id => @tournament.id }, :method => :put
    - else
      = button_to "Cancel your registration for the tournament", { :controller => :signups, :action => :destroy, :id => @tournament.id }, :method => :delete
  - elsif current_user.checked_in?(@tournament)
    p You have already checked in.            
  - elsif @tournament.starts_at > 60.minutes.from_now
    = button_to "Sign up for the tournament", :controller => :signups, :action => :create, :method => :post, :id => @tournament.id
  - else
    p
      | The tournament starts in less than 60 minutes, you can't sign in
- else
  p 
    | You need to 
    |  
    = link_to "log in", login_path
    |  to play

问题是,我不知道如何让这个更干净。我的意思是是的,我可以为按钮添加助手,但这对if if else else 丑陋无济于事,因为有很多不同的组合。这是一个简短的列表:

  • 用户未登录
  • 距离锦标赛开始还有 60 多岁,用户还没有注册锦标赛
  • 距离锦标赛开始还有 60 多岁,并且用户已经注册
  • 不到 60 分钟,但用户尚未注册
  • 不到 60 分钟,用户已注册但尚未签入
  • 不到 60 分钟,用户已经签到

这只是冰山一角,因为管理员应该比普通用户看到更多信息,但我不想让这个问题复杂化。

主要问题是,我应该如何处理这种情况?在视图中这样做似乎很糟糕,但我没有看到任何其他更简单的方法。

【问题讨论】:

  • @coreyward 是的,但这并没有真正帮助解决“太多如果”的事情,它只是把它移到另一个地方
  • 我实际上并没有提交那条半句话的评论,无论如何都是故意的。哈!

标签: ruby-on-rails ruby coding-style view refactoring


【解决方案1】:

更简洁的方法是在模型上创建有意义的方法。例如,在您的锦标赛模型中,添加如下内容:

def can_register?( user )
  !user.registered_for?(self) && self.starts_at > 60.minutes.from_now
end

然后在您看来,您可以在显示某些内容之前检查can_register?。像您那样将逻辑添加到视图中并不是 MVC 应用程序的意图。

【讨论】:

  • 这是一个好的开始。加上将视图逻辑移动到帮助程序,这将是一个非常干净、清晰的重构。
【解决方案2】:

你应该使用一个对象来封装逻辑。也许是这样的:

class UserSignup

  def initialize(user, tournament)
    @user, @tournament = user, tournament
  end

  def registered?
    @user.registered_for?(@tournament)
  end

  def signups_closed?
    @tournament.start_at < 1.hour.from_now
  end

  def checked_in?
    @user.checked_in?(@tournament)
  end

end

这使视图更简单,并且不需要太多工作。您会看到这样会删除很多重复项,并且您可以独立于视图测试您的注册逻辑。

您还可以创建一个演示者,这会涉及更多内容,但会更清晰地显示您的视图。看看像 draper 这样的 gem 来帮助你。

class SignupPresenter

  def initialize(user_signup)
    @user_signup = user_signup
  end

  def register_button
    view.button_to("sign up") if @user_signup.registered?
  end

  # etc ...

end

另外,我会考虑为不同的用户使用不同的模板甚至控制器。因此,根本没有登录的用户甚至无法访问此页面,并且管理员一起拥有不同的控制器(甚至命名空间)。

我不会只是进去把它分成部分,因为那只会隐藏逻辑。我也更喜欢一个单独的对象,而不是把它放到模型中,因为这样模型就不会那么混乱,所有的逻辑都保持在一起,很好地集中。

【讨论】:

  • Rails3(至少)你现在可以使用 Rails Presenter,它们是内置的。
猜你喜欢
  • 1970-01-01
  • 2016-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多