2

在 Rails 中实现“业务规则”的方法是什么?

假设我有一辆车并想卖掉它:

car = Cars.find(24)
car.sell

car.sell方法将检查一些事情:

does current_user own the car?
    check: car.user_id == current_user.id
is the car listed for sale in the sales catalog?
    check: car.catalogs.ids.include? car.id
    
if all o.k. then car is marked as sold.

我正在考虑创建一个名为 Rules 的类:

class Rules
    def initialize(user,car)
        @user = user
        @car = car
    end

    def can_sell_car?
        @car.user_id == @user.id && @car.catalogs.ids.include? @car.id
    end
end

并像这样使用它:

def Car
    def sell
        if Rules.new(current_user,self).can_sell_car
            ..sell the car...
        else
            @error_message = "Cannot sell this car"
            nil
        end
    end
end

至于获取current_user,我正在考虑将其存储在全局变量中?我认为每当调用控制器动作时,它总是一个“新鲜”的调用,对吗?如果是这样,那么将当前用户存储为全局变量不应该带来任何风险..(就像其他用户能够访问其他用户的详细信息一样)

任何见解表示赞赏!

更新

所以,全局变量路由出来了!感谢 PeterWong 指出全局变量仍然存在!

我现在正在考虑使用这种方式:

class Rules
    def self.can_sell_car?(current_user, car)
       ......checks....
    end
end

Rules.can_sell_car?(current_user,@car)然后从控制器动作中调用。对这种新方式有什么想法吗?

4

5 回答 5

3

我会使用下表:

对于买家和卖家:

人(id:int,名称:字符串)

class Person << ActiveRecord::Base
  has_many :cars, :as => :owner
  has_many :sales, :as => :seller, :class_name => 'Transfer'
  has_many :purchases, :as => :buyer, :class_name => 'Transfer'
end

汽车(id:int,owner_id:int,vin:string,year:int,make:string,model:string,listed_at:datetime)

listed_at 是查看汽车是否出售的标志

class Car << ActiveRecord::Base
  belongs_to :owner, :class_name => 'Person'
  has_many :transfers

  def for_sale?
    not listed_at.nil?
  end
end

转移(id:int,car_id:int,seller_id:int,buyer_id:int)

class Transfer << ActiveRecord::Base
  belongs_to :car
  belongs_to :seller, :class_name => 'Person'
  belongs_to :buyer, :class_name => 'Person'

  validates_with Transfer::Validator

  def car_owned_by_seller?
     seller_id == car.owner_id
  end
end

然后你可以使用这个自定义验证器来设置你的规则。

class Transfer::Validator << ActiveModel::Validator
  def validate(transfer)
     transfer.errors[:base] = "Seller doesn't own car" unless transfer.car_owned_by_seller?
     transfer.errors[:base] = "Car isn't for sale" unless transfer.car.for_sale?
  end
end
于 2011-02-15T15:22:54.707 回答
1

首先,标准的 Rails 实践是将所有业务逻辑保留在模型中,而不是控制器中。看起来你正朝着那个方向前进,所以这很好——但是:请注意,没有一种很好的干净方法可以current_user从模型中获取。

我不会制作新的 Rules 模型(尽管如果你真的想这样做,你可以这样做),我只会涉及用户模型和汽车。因此,例如:

class User < ActiveRecord::Base
...
  def sell_car( car )
    if( car.user_id == self.id && car.for_sale? )
      # sell car
    end
  end
...
end

class Car < ActiveRecord::Base
...
  def for_sale?
    !catalog_id.nil?
  end
...
end

显然,我在假设您的目录如何工作,但如果要出售belong_to目录的汽车,那么该方法将起作用 - 否则只需根据需要调整方法以检查汽车是否列在目录中。老实说,在 Car 模型本身上设置一个布尔值可能是个好主意,这样用户就可以在需要时简单地切换要出售或不出售的汽车(通过将汽车标记为待售,或通过将汽车添加到目录等)。

我希望这能给你一些方向!请随时提出问题。


编辑:另一种方法是在您的模型中使用方法,例如:

user.buy_car( car )
car.transfer_to( user )

有很多方法可以将逻辑放入与之交互的对象中。

于 2011-02-15T14:53:05.887 回答
0

我认为这将是使用数据库的主要候选者,然后您可以使用 Ruby 来查询不同的表。

于 2011-02-15T14:34:02.197 回答
0

您可以查看声明性授权 gem - https://github.com/stffn/declarative_authorization

虽然它已针对 CRUD 操作进行了预配置,但您可以轻松添加自己的操作(购买、出售)并将它们的业务逻辑放在 authentication_rules.rb 配置文件中。然后,在您的控制器、视图甚至模型中!您可以轻松地询问 allowed_to? :购买,@汽车

于 2011-02-15T18:51:06.990 回答
0

我正在对用户做类似的事情,以及他们可以对照片库做些什么。我正在使用用户和身份验证的设计,然后我在用户模型中设置了几个方法来确定用户是否具有各种权限(用户通过权限拥有许多画廊)来操作该画廊。我认为您遇到的最大问题似乎是确定当前用户,这可以使用 Devise 轻松处理,然后您可以向用户模型添加一个方法并检查 current_user.can_sell?授权销售。

于 2011-02-15T22:20:28.417 回答