Как реализовать «бизнес-правила» в Rails? - PullRequest
2 голосов
/ 15 февраля 2011

Как реализовать "бизнес-правила" в 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, я думал о сохранении его в глобальной переменной?Я думаю, что всякий раз, когда вызывается действие контроллера, это всегда «свежий» вызов, верно?Если это так, то сохранение текущего пользователя в качестве глобальной переменной не должно представлять каких-либо рисков ... (как, например, некоторые другие пользователи могут получить доступ к данным другого пользователя)

Любые идеи приветствуются!

ОБНОВЛЕНИЕ

Итак, глобальная переменная route отсутствует!Спасибо PeterWong за указание на то, что глобальные переменные сохраняются!

Теперь я думаю об использовании этого способа:

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

И затем вызове Rules.can_sell_car?(current_user,@car) из действия контроллера.Есть мысли по этому новому пути?

Ответы [ 5 ]

3 голосов
/ 15 февраля 2011

Я бы использовал следующие таблицы:

Для покупателей и продавцов:

человек (id: int, name: string)

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: строка, год: int, make: string, model: string, selected_at: datetime)

перечисленный_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, purchase_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 голос
/ 15 февраля 2011

Во-первых, стандартная практика рельсов заключается в том, чтобы сохранять всю бизнес-логику в моделях, а не в контроллерах.Похоже, вы движетесь в этом направлении, так что это хорошо - НО: имейте в виду, нет хорошего чистого способа добраться до 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 )

Есть много способов сделать этологика в объекте его взаимодействия с.

0 голосов
/ 16 февраля 2011

Я делаю что-то подобное с пользователями и что они могут делать с фотогалереями.Я использую devise для пользователей и аутентификации, а затем я настроил несколько методов в модели пользователя, которые определяют, есть ли у пользователя различные разрешения (у пользователей есть много галерей через разрешения), чтобы воздействовать на эту галерею.Я думаю, что, похоже, самая большая проблема, с которой вы столкнулись, связана с определением вашего текущего пользователя, что довольно легко может быть обработано с помощью Devise, а затем вы можете добавить метод в модель пользователя и проверить current_user.can_sell?чтобы разрешить продажу.

0 голосов
/ 15 февраля 2011

Вы можете взглянуть на декларативный камень авторизации - https://github.com/stffn/declarative_authorization

Несмотря на то, что он предварительно настроен для действий CRUD, вы можете легко добавить свои собственные действия (купить, продать) и поместить их бизнес-логику в файл конфигурации authorization_rules.rb. Тогда в ваших контроллерах, представлениях и даже моделях !, вы можете легко спросить allow_to? : купить, @ автомобиль

0 голосов
/ 15 февраля 2011

Я думаю, что это основной кандидат на использование базы данных, и тогда вы могли бы использовать Ruby для запроса различных таблиц.

...