繁体   English   中英

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

[英]How to implement “business rules” in Rails?

在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 ,我正在考虑将其存储在全局变量中? 我认为无论何时调用控制器操作,始终都是“新鲜”调用,对吗? 如果是这样,那么将当前用户存储为全局变量应该不会带来任何风险。(就像其他一些用户能够访问其他用户的详细信息一样)

任何见解表示赞赏!

UPDATE

因此,全局变量路由出局了! 感谢PeterWong指出全局变量仍然存在!

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

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

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

我将使用以下表格:

对于买卖双方:

人(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)

listing_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:整数)

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

首先,标准的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属于目录,那么该方法将起作用-否则,根据需要调整方法,以检查汽车是否在目录中列出。 坦率地说,在Car模型本身上设置布尔值可能是个好主意,这样用户就可以在需要时直接切换要出售或不出售的汽车(通过标记要出售的汽车或将汽车添加到目录等)。

我希望这会给您一些指导! 请随时提问。


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

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

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

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

您可以看一下声明式授权gem- https://github.com/stffn/declarative_authorization

尽管已为CRUD操作预先配置了该操作,但您可以轻松添加自己的操作(购买,出售),并将其业务逻辑放入authorization_rules.rb配置文件中。 然后,在您的控制器,视图甚至模型中!,您可以轻松地问allowed_to? :买,@车

我正在与用户做类似的事情,以及他们可以对照相馆做些什么。 我将devise用于用户和身份验证,然后在用户模型中设置了几种方法,以确定用户是否具有对该画廊采取行动的各种权限(用户通过权限拥有许多画廊)。 我认为您面临的最大问题是确定当前用户,这可以通过Devise轻松处理,然后可以向用户模型添加方法并检查current_user.can_sell? 授权销售。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM