Module: Kernel

Defined in:
lib/contract/integration.rb

Instance Method Summary collapse

Instance Method Details

#adaption(options = {}, &block) ⇒ Object

Adds an adaption route from the specified type to the specified type. Basic usage looks like this:

adaption :from => StringIO, :to => String, :via => :read

This method takes various options. Here’s a complete list:

:from

The type that can be converted from. Defaults to self meaning you can safely omit it in Class, Module or Contract context.

:to

The type that can be converted to. Defaults to self meaning you can safely omit it in Class, Module or Contract context.

Note that you need to specify either :from or :to.

:via

How the :from type will be converted to the :to type. If this is a Symbol the conversion will be done by invoking the method identified by that Symbol on the source object. Otherwise this should be something that responds to the call method (for example Methods and Procs) which will get the source object as its argument and which should return the target object.

:if

The conversion can only be performed if this condition is met. This can either be something that implements the === case equivalence operator or something that implements the call method. So Methods, Procs, Modules, Classes and Contracts all make sense in this context. You can also specify a Symbol in which case the conversion can only be performed if the source object responds to the method identified by that Symbol.

Note that the :if option will default to the same value as the :via option if the :via option is a Symbol.

If you invoke this method with a block it will be used instead of the :via option.

See Contract.adapt for how conversion look-ups are performed.



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/contract/integration.rb', line 512

def adaption(options = {}, &block) # :yield: source_object
  options = {
    :from => self,
    :to => self
  }.merge(options)

  if block then
    if options.include?(:via) then
      raise(ArgumentError, "Can't use both block and :via")
    else
      options[:via] = block
    end
  end

  if options[:via].respond_to?(:to_sym) then
    options[:via] = options[:via].to_sym
  end

  options[:if] ||= options[:via] if options[:via].is_a?(Symbol)

  if options[:via].is_a?(Symbol) then
    symbol = options[:via]
    options[:via] = lambda { |obj| obj.send(symbol) }
  end

  if options[:if].respond_to?(:to_sym) then
    options[:if] = options[:if].to_sym
  end

  if options[:if].is_a?(Symbol) then
    options[:if] = Contract::Check::Quack[options[:if]]
  elsif options[:if].respond_to?(:call) then
    callable = options[:if]
    options[:if] = Contract::Check.block { |obj| callable.call(obj) }
  end

  if options[:from] == self and options[:to] == self then
    raise(ArgumentError, "Need to specify either :from or :to")
  elsif options[:from] == options[:to] then
    raise(ArgumentError, "Self-adaption: :from and :to both are " +
      options[:to].inspect)
  end

  unless options[:via]
    raise(ArgumentError, "Need to specify how to adapt (use :via or block)")
  end

  Contract.adaptions[options[:to]] << options
end