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 <code>:from</code> or
<code>:to</code>.

:via::

How the <code>:from</code> type will be converted to the
<code>:to</code> 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 <code>:if</code> option will default to the same
value as the <code>:via</code> option if the <code>:via</code>
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