简体   繁体   English

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

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

What is the way to implement "business rules" in Rails? 在Rails中实现“业务规则”的方式是什么?

Let us say I have a car and want to sell it: 假设我有一辆车要出售:

car = Cars.find(24)
car.sell

car.sell method will check a few things: 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.

I was thinking of creating a class called Rules: 我当时正在考虑创建一个名为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

And using it like this: 并像这样使用它:

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

As for getting the current_user , I was thinking of storing it in a global variable? 至于获取current_user ,我正在考虑将其存储在全局变量中? I think that whenever a controller action is called, it's always a "fresh" call right? 我认为无论何时调用控制器操作,始终都是“新鲜”调用,对吗? If so then storing the current user as a global variable should not introduce any risks..(like some other user being able to access another user's details) 如果是这样,那么将当前用户存储为全局变量应该不会带来任何风险。(就像其他一些用户能够访问其他用户的详细信息一样)

Any insights are appreciated! 任何见解表示赞赏!

UPDATE UPDATE

So, the global variable route is out! 因此,全局变量路由出局了! Thanks to PeterWong for pointing out that global variables persist! 感谢PeterWong指出全局变量仍然存在!

I've now thinking of using this way: 我现在正在考虑使用这种方式:

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

And then calling Rules.can_sell_car?(current_user,@car) from the controller action. 然后从控制器操作中调用Rules.can_sell_car?(current_user,@car) Any thoughts on this new way? 对这种新方式有什么想法吗?

I'd use the following tables: 我将使用以下表格:

For buyers and sellers: 对于买卖双方:

people(id:int,name:string) 人(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) 汽车(id:int,owner_id:int,vin:string,year:int,make:string,model:string,listed_at:datetime)

listed_at is the flag to see if a Car is for sale or not listing_at是查看汽车是否出售的标志

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

  def for_sale?
    not listed_at.nil?
  end
end

transfers(id:int,car_id:int,seller_id:int,buyer_id:int) 传送(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

Then you can use this custom validator to setup your rules. 然后,您可以使用此自定义验证程序来设置规则。

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

First, the standard rails practice is to keep all business logic in the models, not the controllers. 首先,标准的Rails做法是将所有业务逻辑(而不是控制器)保留在模型中。 It looks like you're heading that direction, so that's good -- BUT: be aware, there isn't a good clean way to get to the current_user from the model. 看起来您正朝着这个方向发展,所以很好-但是:请注意,没有一种很好的干净方法可以从模型中获取current_user

I wouldn't make a new Rules model (although you can if you really want to do it that way), I would just involve the user model and the car. 我不会创建新的规则模型(尽管如果您真的想那样做,可以这样做),但我只会涉及用户模型和汽车。 So, for instance: 因此,例如:

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

Obviously I'm making assumptions about how your Catalog works, but if cars that are for_sale belong_to a catalog, then that method would work - otherwise just adjust the method as necessary to check if the car is listed in a catalog or not. 显然,我正在对目录的工作方式进行假设,但是如果for_sale的belong_to属于目录,那么该方法将起作用-否则,根据需要调整方法,以检查汽车是否在目录中列出。 Honestly it would probably be a good idea to set a boolean value on the Car model itself, this way users could simply toggle the car being for sale or not for sale whenever you want them to ( either by marking the car for sale, or by adding the car to a catalog, etc. ). 坦率地说,在Car模型本身上设置布尔值可能是个好主意,这样用户就可以在需要时直接切换要出售或不出售的汽车(通过标记要出售的汽车或将汽车添加到目录等)。

I hope this gives you some direction! 我希望这会给您一些指导! Please feel free to ask questions. 请随时提问。


EDIT: Another way to do this would be to have methods in your models like: 编辑:另一种方法是在模型中使用如下方法:

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

There are many ways to do it putting the logic in the object its interacting with. 有很多方法可以将逻辑放入与其交互的对象中。

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

You might take a look at the declarative authorization gem - https://github.com/stffn/declarative_authorization 您可以看一下声明式授权gem- https://github.com/stffn/declarative_authorization

While it's pre-configured for CRUD actions, you can easily add your own actions (buy, sell) and put their business logic in the authorization_rules.rb config file. 尽管已为CRUD操作预先配置了该操作,但您可以轻松添加自己的操作(购买,出售),并将其业务逻辑放入authorization_rules.rb配置文件中。 Then, in your controllers, views, and even models!, you can easily ask permitted_to? 然后,在您的控制器,视图甚至模型中!,您可以轻松地问allowed_to? :buy, @car :买,@车

I'm doing something similar with users and what they can do with photo galleries. 我正在与用户做类似的事情,以及他们可以对照相馆做些什么。 I'm using devise for users and authentication, and then I set up several methods in the user model that determine if the user has various permissions (users have many galleries through permissions) to act on that gallery. 我将devise用于用户和身份验证,然后在用户模型中设置了几种方法,以确定用户是否具有对该画廊采取行动的各种权限(用户通过权限拥有许多画廊)。 I think it looks like the biggest problem you are having is with determining your current user, which can be handled quite easily with Devise, and then you can add a method to the user model and check current_user.can_sell? 我认为您面临的最大问题是确定当前用户,这可以通过Devise轻松处理,然后可以向用户模型添加方法并检查current_user.can_sell? to authorized a sale. 授权销售。

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

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