Автор: Давыденков Михаил
Основная идея, история и требования к языку
Что входит в контракт метода или функции
Контракты в Ruby
gem install contracts # inspired by contracts.coffee
require 'contracts' # в application.rb
include Contracts
Contract Num => Num
def double(x)
x * 2
end
puts double("oops")
Пример нарушения контракта
./contracts.rb:34:in 'failure_callback': Contract violation: (RuntimeError)
Expected: Contracts::Num,
Actual: "oops"
Value guarded in: Object::double
With Contract: Contracts::Num, Contracts::Num
At: main.rb:6
...stack trace...
Структура failure_message
{
arg: the argument to the method,
contract: the contract that got violated,
class: the method's class,
method: the method,
contracts: the contract object
}
Встроенные контракты
contracts.ruby предусматривает большое количество встроенных контрактов:
Num, Pos, Neg, Nat, Bool, Any, None,
Or, Xor, Not,
ArrayOf, HashOf, Maybe,
RespondTo[:password, :credit_card], Send[:valid?], Exactly[Numeric]
Создание собственных контрактов
Контракты очень просто создать. Контрактом может быть:
Кастомизации
Переписывание failure_callback
# initializer
Contract.override_failure_callback do |data|
Rails.logger.error format(data)
Airbrake.notify_or_ignore(error_from_data(data))
end
Переписывание сообщений об ошибках
def Num.to_s
"a number please"
end
Pattern matching
Wihtout
Contract Num => Num
def fact x
if x == 1
x
else
x * fact(x - 1)
end
end
With
Contract 1 => 1
def fact x
x
end
Contract Num => Num
def fact x
x * fact(x - 1)
end
Pattern matching 2
Contract lambda{|n| n < 12 } => Ticket
def get_ticket(age)
ChildTicket.new(age: age)
end
Contract lambda{|n| n >= 12 } => Ticket
def get_ticket(age)
AdultTicket.new(age: age)
end
Контракты в модулях
module M
include Contracts
include Contracts::Modules
Contract String => String
def self.parse
# do some hard parsing
end
end
Инварианты
include Contracts::Invariants
Invariant(:day) { 1 <= day && day <= 31 }
Invariant(:month) { 1 <= month && month <= 12 }
Contract None => Fixnum
def silly_next_day!
self.day += 1
end
Производительность
Заявлено, что контракты имееют минимальный slowdown по производительности. Бенчмарки метода, возвращающего сумму двух чисел (1_000_000 раз):
total real
testing read 2.530000 ( 2.521314)
testing contracts read 2.900000 ( 2.903721)
По-моему, этот гем - хорошое начинание и может быть применён по ситуации (в маленьких хоум проджектах). Правда придётся чаще пользоваться бенчмарками, т.к. 100% доверия гему нет. Библиотеки с ним особо не попишешь, т.к. есть несколько пока нерешённых issues, связанные с динамическим созданием кода
Пример реализации контрактов в rails приложении