如何在Rails中实现“业务规则”?

时间:2011-02-15 13:42:08

标签: ruby-on-rails ruby-on-rails-3 business-logic-layer

在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)。 有关这种新方式的任何想法吗?

5 个答案:

答案 0 :(得分: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

cars(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

答案 1 :(得分:1)

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

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

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

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

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


编辑:另一种方法是在模型中使用方法:

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

虽然它是为CRUD操作预先配置的,但您可以轻松添加自己的操作(购买,销售)并将其业务逻辑放在authorization_rules.rb配置文件中。然后,在您的控制器,视图甚至模型中,您可以轻松地询问allowed_to? :买,@ car

答案 4 :(得分:0)

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