⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 15 - enforcing software contracts.rb

📁 O Reilly Ruby Cookbook source code
💻 RB
字号:
module Contracts  def valid_contract(input)    if @user_defined and @user_defined[input]      @user_defined[input]    else      case input      when :number        lambda { |x| x.is_a? Numeric }      when :string        lambda { |x| x.respond_to? :to_str }      when :anything        lambda { |x| true }      else        lambda { |x| false }      end    end  end  class ContractViolation < StandardError  end     def define_data(inputs={}.freeze)    @user_defined ||= {}    inputs.each do |name, contract|      @user_defined[name] = contract if contract.respond_to? :call    end  end  def contract(method, *inputs)    @contracts ||= {}    @contracts[method] = inputs    method_added(method)  end   def setup_contract(method, inputs)    @contracts[method] = nil    method_renamed = "__#{method}".intern        conditions = ""    inputs.flatten.each_with_index do |input, i|      conditions << %{       if not self.class.valid_contract(#{input.inspect}).call(args[#{i}])          raise ContractViolation, "argument #{i+1} of method '#{method}' must satisfy the '#{input}' contract", caller        end      }    end   class_eval %{       alias_method #{method_renamed.inspect}, #{method.inspect}       def #{method}(*args)         #{conditions}         return #{method_renamed}(*args)       end     }  end  def method_added(method)    inputs = @contracts[method]    setup_contract(method, inputs) if inputs  endend#---class TestContracts  def hello(n, s, f)    n.times { f.write "hello #{s}!\n" }  end#---  extend Contracts    writable_and_open = lambda do |x|     x.respond_to?('write') and x.respond_to?('closed?') and not x.closed?  end  define_data(:writable => writable_and_open,              :positive => lambda {|x| x >= 0 })#---  contract :hello, [:positive, :string, :writable]end#---tc = TestContracts.newtc.hello(2, 'world', $stdout)# hello world!# hello world!tc.hello(-1, 'world', $stdout)# Contracts::ContractViolation: argument 1 of method 'hello' must satisfy the# 'positive' contracttc.hello(2, 3001, $stdout)# test-contracts.rb:22: argument 2 of method 'hello' must satisfy the # 'string' contract (Contracts::ContractViolation)closed_file = open('file.txt', 'w') { }tc.hello(2, 'world', closed_file)# Contracts::ContractViolation: argument 3 of method 'hello' must satisfy the # 'writable' contract#---contract :hello, [:positive, :string, :writable]#---def hello(n,s,f)  if not (n >= 0)    raise ContractViolation,     "argument 1 of method 'hello' must satisfy the 'positive' contract", caller  end  if not (s.respond_to? String)    raise ContractViolation,    "argument 2 of method 'hello' must satisfy the 'string' contract",     caller  end  if not (f.respond_to?('write') and f.respond_to?('closed?')           and not f.closed?)    raise ContractViolation,     "argument 3 of method 'hello' must satisfy the 'writable' contract",    caller  end  return __hello(n,s,f)enddef __hello(n,s,f)  n.times { f.write "hello #{s}!\n" }end#---

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -